diff --git a/.editorconfig b/.editorconfig index 68d4dba65..a20727a77 100644 --- a/.editorconfig +++ b/.editorconfig @@ -14,6 +14,4 @@ insert_final_newline = true # Change these settings to your own preference indent_style = space indent_size = 2 - -[*.md] -indent_size = 4 +max_line_length = 80 diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 000000000..d99a8f2a6 --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,48 @@ +/** + * See also + * https://github.com/vuepress/vuepress-next/blob/961228234e3983f1f84f992a1317316d09f8cb98/.eslintrc.js#L1 + * https://github.com/vuejs/eslint-config-airbnb/blob/master/index.js + */ +module.exports = { + root: true, + extends: ['vuepress', 'airbnb-base'], + settings: { + 'import/extensions': ['.js', '.jsx', '.mjs', '.ts', '.tsx'], + }, + rules: { + 'import/extensions': [ + 'error', + 'always', + { + js: 'never', + mjs: 'never', + jsx: 'never', + ts: 'never', + tsx: 'never', + }, + ], + // 'no-param-reassign': [ + // 'error', + // { + // props: true, + // ignorePropertyModificationsFor: [ + // 'state', // for vuex state + // 'acc', // for reduce accumulators + // 'e', // for e.returnvalue + // ], + // }, + // ], + + /** + * Our rules + */ + 'no-console': 'error', + 'import/no-extraneous-dependencies': 'off', + 'no-underscore-dangle': 'off', + 'no-param-reassign': 'off', + 'no-use-before-define': 'off', + + // Set by Prettier + 'max-len': 'off', + }, +}; diff --git a/.eslintrc.yml b/.eslintrc.yml deleted file mode 100644 index 8b3e01140..000000000 --- a/.eslintrc.yml +++ /dev/null @@ -1,69 +0,0 @@ -env: - browser: true - -parser: babel-eslint - -parserOptions: - ecmaVersion: 6 - -# plugins: -# - import - -extends: - - eslint:all - # https://prettier.io/docs/en/integrating-with-linters.html - - plugin:prettier/recommended - -# THIS RULES ARE ORGANIZED AS IN THE PAGE https://eslint.org/docs/rules/ -rules: - # ============================================================================ - # Strict Mode - # ============================================================================ - - # This rule requires or disallows strict mode directives. - # global: requires one strict mode directive in the global scope (and disallows any other strict mode directives) - strict: - - error - - global - - # ============================================================================ - # Stylistic Issues - # ============================================================================ - capitalized-comments: off - indent: - - error - - 2 - - max-statements: - - error - - max: 20 - # Does not work with IIFE - # https://github.com/eslint/eslint/issues/12950 - - ignoreTopLevelFunctions: true - - max-lines-per-function: - - error - - max: 60 - - max-lines: - - error - - max: 400 - - lines-around-comment: off - - no-ternary: off - - # enforce variables to be declared either together or separately in functions - # [when we declare several array functions in a file, give an error] - one-var: off - - # Give use false-positive into ./site/themes/sogilis/static/js/scripts.js - padded-blocks: off - - quotes: - - error - - single - - wrap-iife: - - error - - inside diff --git a/.github/workflows/scheduled.yml b/.github/workflows/scheduled.yml index 0b5ee2f7b..8e0c30ea2 100644 --- a/.github/workflows/scheduled.yml +++ b/.github/workflows/scheduled.yml @@ -28,8 +28,8 @@ jobs: env: SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_URL }} SLACK_USERNAME: Blog - SLACK_ICON_EMOJI: ":warning:" - SLACK_COLOR: "#A30101" + SLACK_ICON_EMOJI: ':warning:' + SLACK_COLOR: '#A30101' SLACK_TITLE: Dead link checker SLACK_MESSAGE: Blog contains dead link(s) :boom:. Please fix this quickly to prevent SEO degradation. SLACK_FOOTER: Scheduled job executed by Github Actions diff --git a/.gitignore b/.gitignore index 47b0a35db..be46dcc79 100644 --- a/.gitignore +++ b/.gitignore @@ -18,19 +18,16 @@ # Check https://zzz.buzz/2018/05/23/differences-of-rules-between-gitignore-and-dockerignore/ /node_modules/ -/dist/ -/export/ /npm-debug.log /yarn-error.log -# You can download manually Hugo binaries here -/bin/ +# Generated by VuePress +/blog/.vuepress/.temp/ +/blog/.vuepress/.cache/ +/blog/.vuepress/dist/ -# Generated resources (like css from scss) -/site/resources/_gen - -# ./site/data/webpack.json is generated -/site/data/ +# Needed for https://github.com/vuepress/configs/tree/master/packages/eslint-config-vuepress-typescript +!/blog/.vuepress # Idea IntelliJ /.idea/ @@ -39,17 +36,5 @@ # to avoid accidental push of this file. /package-lock.json -# External dependencies copied from ./node_modules that we won't have in our -# repo Git -/site/themes/sogilis/assets/css/reboot.min.css -/site/themes/sogilis/assets/css/reboot.min.css.map -/site/themes/sogilis/static/css/typeface-eb-garamond/ -/site/themes/sogilis/static/css/typeface-montserrat/ -/site/static/admin/netlify-cms.js -/site/static/admin/netlify-cms.js.map - # VS Code editor .vscode - -# macOS files -./.DS_Store diff --git a/.husky/.gitignore b/.husky/.gitignore new file mode 100644 index 000000000..c9cdc63b0 --- /dev/null +++ b/.husky/.gitignore @@ -0,0 +1 @@ +_ \ No newline at end of file diff --git a/.husky/_/husky.sh b/.husky/_/husky.sh new file mode 100644 index 000000000..ca2720e08 --- /dev/null +++ b/.husky/_/husky.sh @@ -0,0 +1,30 @@ +#!/bin/sh +if [ -z "$husky_skip_init" ]; then + debug () { + [ "$HUSKY_DEBUG" = "1" ] && echo "husky (debug) - $1" + } + + readonly hook_name="$(basename "$0")" + debug "starting $hook_name..." + + if [ "$HUSKY" = "0" ]; then + debug "HUSKY env variable is set to 0, skipping hook" + exit 0 + fi + + if [ -f ~/.huskyrc ]; then + debug "sourcing ~/.huskyrc" + . ~/.huskyrc + fi + + export readonly husky_skip_init=1 + sh -e "$0" "$@" + exitCode="$?" + + if [ $exitCode != 0 ]; then + echo "husky - $hook_name hook exited with code $exitCode (error)" + exit $exitCode + fi + + exit 0 +fi diff --git a/.husky/pre-commit b/.husky/pre-commit new file mode 100755 index 000000000..59a32c7c4 --- /dev/null +++ b/.husky/pre-commit @@ -0,0 +1,4 @@ +#!/bin/sh +. "$(dirname "$0")/_/husky.sh" + +yarn test && yarn sort-package-json \ No newline at end of file diff --git a/.prettierrc.js b/.prettierrc.js new file mode 100644 index 000000000..e27094149 --- /dev/null +++ b/.prettierrc.js @@ -0,0 +1,67 @@ +/** + * See also + * https://github.com/vuepress/vuepress-next/blob/961228234e3983f1f84f992a1317316d09f8cb98/package.json#L32 + * https://github.com/vuepress/configs/blob/master/packages/prettier-config-vuepress/lib/index.js + */ +module.exports = { + // Maximum line length + printWidth: 80, + + // Specify the number of spaces per indentation-level + tabWidth: 2, + + // Indent lines with tabs instead of spaces + useTabs: false, + + // Use semicolons or not + semi: true, + + // Use single quotes instead of double quotes + singleQuote: true, + + // Change when properties in objects are quoted + quoteProps: 'consistent', + + // Use single quotes instead of double quotes in JSX + jsxSingleQuote: true, + + // Print trailing commas wherever possible when multi-line + trailingComma: 'es5', + + // Print spaces between brackets in object literals. + bracketSpacing: true, + + // Put the `>` of a multi-line JSX element at the end of the last line instead of being alone on the next line (does not apply to self closing elements) + jsxBracketSameLine: false, + + // Include parentheses around a sole arrow function parameter + arrowParens: 'always', + + // Format only a segment of a file. + rangeStart: 0, + rangeEnd: Infinity, + + // Specify which parser to use. + // parser: undefined, + + // Specify the file name to use to infer which parser to use. + // filepath: undefined, + + // Prettier can restrict itself to only format files that contain a special comment, called a pragma, at the top of the file. + requirePragma: false, + + // Prettier can insert a special @format marker at the top of files specifying that the file has been formatted with prettier. + insertPragma: false, + + // By default, Prettier will wrap markdown text as-is since some services use a line-break-sensitive renderer, e.g. GitHub comment and BitBucket. + proseWrap: 'preserve', + + // Specify the global whitespace sensitivity for HTML files + htmlWhitespaceSensitivity: 'css', + + // Whether or not to indent the code inside + + diff --git a/blog/.vuepress/components/VideoIframe.vue b/blog/.vuepress/components/VideoIframe.vue new file mode 100644 index 000000000..f9c27379a --- /dev/null +++ b/blog/.vuepress/components/VideoIframe.vue @@ -0,0 +1,29 @@ + + + + + diff --git a/blog/.vuepress/config.js b/blog/.vuepress/config.js new file mode 100644 index 000000000..82df3f7fd --- /dev/null +++ b/blog/.vuepress/config.js @@ -0,0 +1,18 @@ +const path = require('path'); + +module.exports = { + title: 'Blog Site', + description: + "Série d'articles écrits par les membres de Sogilis décrivants la vie en entreprise ou des sujets techniques précis", + base: '/', + head: [['link', { rel: 'icon', href: '/favicon.png' }]], + theme: path.join(__dirname, '/../../theme/'), + // Configuration of Markdown should not be in ../../theme/index + markdown: { + lineNumbers: true, + extendMarkdown: (md) => { + // eslint-disable-next-line global-require + md.use(require('markdown-it-footnote')); + }, + }, +}; diff --git a/site/themes/sogilis/static/fonts/JetBrainsMono/LICENSE b/blog/.vuepress/public/fonts/JetBrainsMono/LICENSE similarity index 100% rename from site/themes/sogilis/static/fonts/JetBrainsMono/LICENSE rename to blog/.vuepress/public/fonts/JetBrainsMono/LICENSE diff --git a/site/themes/sogilis/static/fonts/JetBrainsMono/eot/JetBrainsMono-Bold-Italic.eot b/blog/.vuepress/public/fonts/JetBrainsMono/eot/JetBrainsMono-Bold-Italic.eot similarity index 100% rename from site/themes/sogilis/static/fonts/JetBrainsMono/eot/JetBrainsMono-Bold-Italic.eot rename to blog/.vuepress/public/fonts/JetBrainsMono/eot/JetBrainsMono-Bold-Italic.eot diff --git a/site/themes/sogilis/static/fonts/JetBrainsMono/eot/JetBrainsMono-Bold.eot b/blog/.vuepress/public/fonts/JetBrainsMono/eot/JetBrainsMono-Bold.eot similarity index 100% rename from site/themes/sogilis/static/fonts/JetBrainsMono/eot/JetBrainsMono-Bold.eot rename to blog/.vuepress/public/fonts/JetBrainsMono/eot/JetBrainsMono-Bold.eot diff --git a/site/themes/sogilis/static/fonts/JetBrainsMono/eot/JetBrainsMono-ExtraBold-Italic.eot b/blog/.vuepress/public/fonts/JetBrainsMono/eot/JetBrainsMono-ExtraBold-Italic.eot similarity index 100% rename from site/themes/sogilis/static/fonts/JetBrainsMono/eot/JetBrainsMono-ExtraBold-Italic.eot rename to blog/.vuepress/public/fonts/JetBrainsMono/eot/JetBrainsMono-ExtraBold-Italic.eot diff --git a/site/themes/sogilis/static/fonts/JetBrainsMono/eot/JetBrainsMono-ExtraBold.eot b/blog/.vuepress/public/fonts/JetBrainsMono/eot/JetBrainsMono-ExtraBold.eot similarity index 100% rename from site/themes/sogilis/static/fonts/JetBrainsMono/eot/JetBrainsMono-ExtraBold.eot rename to blog/.vuepress/public/fonts/JetBrainsMono/eot/JetBrainsMono-ExtraBold.eot diff --git a/site/themes/sogilis/static/fonts/JetBrainsMono/eot/JetBrainsMono-ExtraLight-Italic.eot b/blog/.vuepress/public/fonts/JetBrainsMono/eot/JetBrainsMono-ExtraLight-Italic.eot similarity index 100% rename from site/themes/sogilis/static/fonts/JetBrainsMono/eot/JetBrainsMono-ExtraLight-Italic.eot rename to blog/.vuepress/public/fonts/JetBrainsMono/eot/JetBrainsMono-ExtraLight-Italic.eot diff --git a/site/themes/sogilis/static/fonts/JetBrainsMono/eot/JetBrainsMono-ExtraLight.eot b/blog/.vuepress/public/fonts/JetBrainsMono/eot/JetBrainsMono-ExtraLight.eot similarity index 100% rename from site/themes/sogilis/static/fonts/JetBrainsMono/eot/JetBrainsMono-ExtraLight.eot rename to blog/.vuepress/public/fonts/JetBrainsMono/eot/JetBrainsMono-ExtraLight.eot diff --git a/site/themes/sogilis/static/fonts/JetBrainsMono/eot/JetBrainsMono-Italic.eot b/blog/.vuepress/public/fonts/JetBrainsMono/eot/JetBrainsMono-Italic.eot similarity index 100% rename from site/themes/sogilis/static/fonts/JetBrainsMono/eot/JetBrainsMono-Italic.eot rename to blog/.vuepress/public/fonts/JetBrainsMono/eot/JetBrainsMono-Italic.eot diff --git a/site/themes/sogilis/static/fonts/JetBrainsMono/eot/JetBrainsMono-Light-Italic.eot b/blog/.vuepress/public/fonts/JetBrainsMono/eot/JetBrainsMono-Light-Italic.eot similarity index 100% rename from site/themes/sogilis/static/fonts/JetBrainsMono/eot/JetBrainsMono-Light-Italic.eot rename to blog/.vuepress/public/fonts/JetBrainsMono/eot/JetBrainsMono-Light-Italic.eot diff --git a/site/themes/sogilis/static/fonts/JetBrainsMono/eot/JetBrainsMono-Light.eot b/blog/.vuepress/public/fonts/JetBrainsMono/eot/JetBrainsMono-Light.eot similarity index 100% rename from site/themes/sogilis/static/fonts/JetBrainsMono/eot/JetBrainsMono-Light.eot rename to blog/.vuepress/public/fonts/JetBrainsMono/eot/JetBrainsMono-Light.eot diff --git a/site/themes/sogilis/static/fonts/JetBrainsMono/eot/JetBrainsMono-Medium-Italic.eot b/blog/.vuepress/public/fonts/JetBrainsMono/eot/JetBrainsMono-Medium-Italic.eot similarity index 100% rename from site/themes/sogilis/static/fonts/JetBrainsMono/eot/JetBrainsMono-Medium-Italic.eot rename to blog/.vuepress/public/fonts/JetBrainsMono/eot/JetBrainsMono-Medium-Italic.eot diff --git a/site/themes/sogilis/static/fonts/JetBrainsMono/eot/JetBrainsMono-Medium.eot b/blog/.vuepress/public/fonts/JetBrainsMono/eot/JetBrainsMono-Medium.eot similarity index 100% rename from site/themes/sogilis/static/fonts/JetBrainsMono/eot/JetBrainsMono-Medium.eot rename to blog/.vuepress/public/fonts/JetBrainsMono/eot/JetBrainsMono-Medium.eot diff --git a/site/themes/sogilis/static/fonts/JetBrainsMono/eot/JetBrainsMono-Regular.eot b/blog/.vuepress/public/fonts/JetBrainsMono/eot/JetBrainsMono-Regular.eot similarity index 100% rename from site/themes/sogilis/static/fonts/JetBrainsMono/eot/JetBrainsMono-Regular.eot rename to blog/.vuepress/public/fonts/JetBrainsMono/eot/JetBrainsMono-Regular.eot diff --git a/site/themes/sogilis/static/fonts/JetBrainsMono/eot/JetBrainsMono-SemiLight-Italic.eot b/blog/.vuepress/public/fonts/JetBrainsMono/eot/JetBrainsMono-SemiLight-Italic.eot similarity index 100% rename from site/themes/sogilis/static/fonts/JetBrainsMono/eot/JetBrainsMono-SemiLight-Italic.eot rename to blog/.vuepress/public/fonts/JetBrainsMono/eot/JetBrainsMono-SemiLight-Italic.eot diff --git a/site/themes/sogilis/static/fonts/JetBrainsMono/eot/JetBrainsMono-SemiLight.eot b/blog/.vuepress/public/fonts/JetBrainsMono/eot/JetBrainsMono-SemiLight.eot similarity index 100% rename from site/themes/sogilis/static/fonts/JetBrainsMono/eot/JetBrainsMono-SemiLight.eot rename to blog/.vuepress/public/fonts/JetBrainsMono/eot/JetBrainsMono-SemiLight.eot diff --git a/site/themes/sogilis/static/fonts/JetBrainsMono/version.txt b/blog/.vuepress/public/fonts/JetBrainsMono/version.txt similarity index 100% rename from site/themes/sogilis/static/fonts/JetBrainsMono/version.txt rename to blog/.vuepress/public/fonts/JetBrainsMono/version.txt diff --git a/site/themes/sogilis/static/fonts/JetBrainsMono/woff/JetBrainsMono-Bold-Italic.woff b/blog/.vuepress/public/fonts/JetBrainsMono/woff/JetBrainsMono-Bold-Italic.woff similarity index 100% rename from site/themes/sogilis/static/fonts/JetBrainsMono/woff/JetBrainsMono-Bold-Italic.woff rename to blog/.vuepress/public/fonts/JetBrainsMono/woff/JetBrainsMono-Bold-Italic.woff diff --git a/site/themes/sogilis/static/fonts/JetBrainsMono/woff/JetBrainsMono-Bold.woff b/blog/.vuepress/public/fonts/JetBrainsMono/woff/JetBrainsMono-Bold.woff similarity index 100% rename from site/themes/sogilis/static/fonts/JetBrainsMono/woff/JetBrainsMono-Bold.woff rename to blog/.vuepress/public/fonts/JetBrainsMono/woff/JetBrainsMono-Bold.woff diff --git a/site/themes/sogilis/static/fonts/JetBrainsMono/woff/JetBrainsMono-ExtraBold-Italic.woff b/blog/.vuepress/public/fonts/JetBrainsMono/woff/JetBrainsMono-ExtraBold-Italic.woff similarity index 100% rename from site/themes/sogilis/static/fonts/JetBrainsMono/woff/JetBrainsMono-ExtraBold-Italic.woff rename to blog/.vuepress/public/fonts/JetBrainsMono/woff/JetBrainsMono-ExtraBold-Italic.woff diff --git a/site/themes/sogilis/static/fonts/JetBrainsMono/woff/JetBrainsMono-ExtraBold.woff b/blog/.vuepress/public/fonts/JetBrainsMono/woff/JetBrainsMono-ExtraBold.woff similarity index 100% rename from site/themes/sogilis/static/fonts/JetBrainsMono/woff/JetBrainsMono-ExtraBold.woff rename to blog/.vuepress/public/fonts/JetBrainsMono/woff/JetBrainsMono-ExtraBold.woff diff --git a/site/themes/sogilis/static/fonts/JetBrainsMono/woff/JetBrainsMono-ExtraLight-Italic.woff b/blog/.vuepress/public/fonts/JetBrainsMono/woff/JetBrainsMono-ExtraLight-Italic.woff similarity index 100% rename from site/themes/sogilis/static/fonts/JetBrainsMono/woff/JetBrainsMono-ExtraLight-Italic.woff rename to blog/.vuepress/public/fonts/JetBrainsMono/woff/JetBrainsMono-ExtraLight-Italic.woff diff --git a/site/themes/sogilis/static/fonts/JetBrainsMono/woff/JetBrainsMono-ExtraLight.woff b/blog/.vuepress/public/fonts/JetBrainsMono/woff/JetBrainsMono-ExtraLight.woff similarity index 100% rename from site/themes/sogilis/static/fonts/JetBrainsMono/woff/JetBrainsMono-ExtraLight.woff rename to blog/.vuepress/public/fonts/JetBrainsMono/woff/JetBrainsMono-ExtraLight.woff diff --git a/site/themes/sogilis/static/fonts/JetBrainsMono/woff/JetBrainsMono-Italic.woff b/blog/.vuepress/public/fonts/JetBrainsMono/woff/JetBrainsMono-Italic.woff similarity index 100% rename from site/themes/sogilis/static/fonts/JetBrainsMono/woff/JetBrainsMono-Italic.woff rename to blog/.vuepress/public/fonts/JetBrainsMono/woff/JetBrainsMono-Italic.woff diff --git a/site/themes/sogilis/static/fonts/JetBrainsMono/woff/JetBrainsMono-Light-Italic.woff b/blog/.vuepress/public/fonts/JetBrainsMono/woff/JetBrainsMono-Light-Italic.woff similarity index 100% rename from site/themes/sogilis/static/fonts/JetBrainsMono/woff/JetBrainsMono-Light-Italic.woff rename to blog/.vuepress/public/fonts/JetBrainsMono/woff/JetBrainsMono-Light-Italic.woff diff --git a/site/themes/sogilis/static/fonts/JetBrainsMono/woff/JetBrainsMono-Light.woff b/blog/.vuepress/public/fonts/JetBrainsMono/woff/JetBrainsMono-Light.woff similarity index 100% rename from site/themes/sogilis/static/fonts/JetBrainsMono/woff/JetBrainsMono-Light.woff rename to blog/.vuepress/public/fonts/JetBrainsMono/woff/JetBrainsMono-Light.woff diff --git a/site/themes/sogilis/static/fonts/JetBrainsMono/woff/JetBrainsMono-Medium-Italic.woff b/blog/.vuepress/public/fonts/JetBrainsMono/woff/JetBrainsMono-Medium-Italic.woff similarity index 100% rename from site/themes/sogilis/static/fonts/JetBrainsMono/woff/JetBrainsMono-Medium-Italic.woff rename to blog/.vuepress/public/fonts/JetBrainsMono/woff/JetBrainsMono-Medium-Italic.woff diff --git a/site/themes/sogilis/static/fonts/JetBrainsMono/woff/JetBrainsMono-Medium.woff b/blog/.vuepress/public/fonts/JetBrainsMono/woff/JetBrainsMono-Medium.woff similarity index 100% rename from site/themes/sogilis/static/fonts/JetBrainsMono/woff/JetBrainsMono-Medium.woff rename to blog/.vuepress/public/fonts/JetBrainsMono/woff/JetBrainsMono-Medium.woff diff --git a/site/themes/sogilis/static/fonts/JetBrainsMono/woff/JetBrainsMono-Regular.woff b/blog/.vuepress/public/fonts/JetBrainsMono/woff/JetBrainsMono-Regular.woff similarity index 100% rename from site/themes/sogilis/static/fonts/JetBrainsMono/woff/JetBrainsMono-Regular.woff rename to blog/.vuepress/public/fonts/JetBrainsMono/woff/JetBrainsMono-Regular.woff diff --git a/site/themes/sogilis/static/fonts/JetBrainsMono/woff/JetBrainsMono-SemiLight-Italic.woff b/blog/.vuepress/public/fonts/JetBrainsMono/woff/JetBrainsMono-SemiLight-Italic.woff similarity index 100% rename from site/themes/sogilis/static/fonts/JetBrainsMono/woff/JetBrainsMono-SemiLight-Italic.woff rename to blog/.vuepress/public/fonts/JetBrainsMono/woff/JetBrainsMono-SemiLight-Italic.woff diff --git a/site/themes/sogilis/static/fonts/JetBrainsMono/woff/JetBrainsMono-SemiLight.woff b/blog/.vuepress/public/fonts/JetBrainsMono/woff/JetBrainsMono-SemiLight.woff similarity index 100% rename from site/themes/sogilis/static/fonts/JetBrainsMono/woff/JetBrainsMono-SemiLight.woff rename to blog/.vuepress/public/fonts/JetBrainsMono/woff/JetBrainsMono-SemiLight.woff diff --git a/site/themes/sogilis/static/fonts/JetBrainsMono/woff2/JetBrainsMono-Bold-Italic.woff2 b/blog/.vuepress/public/fonts/JetBrainsMono/woff2/JetBrainsMono-Bold-Italic.woff2 similarity index 100% rename from site/themes/sogilis/static/fonts/JetBrainsMono/woff2/JetBrainsMono-Bold-Italic.woff2 rename to blog/.vuepress/public/fonts/JetBrainsMono/woff2/JetBrainsMono-Bold-Italic.woff2 diff --git a/site/themes/sogilis/static/fonts/JetBrainsMono/woff2/JetBrainsMono-Bold.woff2 b/blog/.vuepress/public/fonts/JetBrainsMono/woff2/JetBrainsMono-Bold.woff2 similarity index 100% rename from site/themes/sogilis/static/fonts/JetBrainsMono/woff2/JetBrainsMono-Bold.woff2 rename to blog/.vuepress/public/fonts/JetBrainsMono/woff2/JetBrainsMono-Bold.woff2 diff --git a/site/themes/sogilis/static/fonts/JetBrainsMono/woff2/JetBrainsMono-ExtraBold-Italic.woff2 b/blog/.vuepress/public/fonts/JetBrainsMono/woff2/JetBrainsMono-ExtraBold-Italic.woff2 similarity index 100% rename from site/themes/sogilis/static/fonts/JetBrainsMono/woff2/JetBrainsMono-ExtraBold-Italic.woff2 rename to blog/.vuepress/public/fonts/JetBrainsMono/woff2/JetBrainsMono-ExtraBold-Italic.woff2 diff --git a/site/themes/sogilis/static/fonts/JetBrainsMono/woff2/JetBrainsMono-ExtraBold.woff2 b/blog/.vuepress/public/fonts/JetBrainsMono/woff2/JetBrainsMono-ExtraBold.woff2 similarity index 100% rename from site/themes/sogilis/static/fonts/JetBrainsMono/woff2/JetBrainsMono-ExtraBold.woff2 rename to blog/.vuepress/public/fonts/JetBrainsMono/woff2/JetBrainsMono-ExtraBold.woff2 diff --git a/site/themes/sogilis/static/fonts/JetBrainsMono/woff2/JetBrainsMono-ExtraLight-Italic.woff2 b/blog/.vuepress/public/fonts/JetBrainsMono/woff2/JetBrainsMono-ExtraLight-Italic.woff2 similarity index 100% rename from site/themes/sogilis/static/fonts/JetBrainsMono/woff2/JetBrainsMono-ExtraLight-Italic.woff2 rename to blog/.vuepress/public/fonts/JetBrainsMono/woff2/JetBrainsMono-ExtraLight-Italic.woff2 diff --git a/site/themes/sogilis/static/fonts/JetBrainsMono/woff2/JetBrainsMono-ExtraLight.woff2 b/blog/.vuepress/public/fonts/JetBrainsMono/woff2/JetBrainsMono-ExtraLight.woff2 similarity index 100% rename from site/themes/sogilis/static/fonts/JetBrainsMono/woff2/JetBrainsMono-ExtraLight.woff2 rename to blog/.vuepress/public/fonts/JetBrainsMono/woff2/JetBrainsMono-ExtraLight.woff2 diff --git a/site/themes/sogilis/static/fonts/JetBrainsMono/woff2/JetBrainsMono-Italic.woff2 b/blog/.vuepress/public/fonts/JetBrainsMono/woff2/JetBrainsMono-Italic.woff2 similarity index 100% rename from site/themes/sogilis/static/fonts/JetBrainsMono/woff2/JetBrainsMono-Italic.woff2 rename to blog/.vuepress/public/fonts/JetBrainsMono/woff2/JetBrainsMono-Italic.woff2 diff --git a/site/themes/sogilis/static/fonts/JetBrainsMono/woff2/JetBrainsMono-Light-Italic.woff2 b/blog/.vuepress/public/fonts/JetBrainsMono/woff2/JetBrainsMono-Light-Italic.woff2 similarity index 100% rename from site/themes/sogilis/static/fonts/JetBrainsMono/woff2/JetBrainsMono-Light-Italic.woff2 rename to blog/.vuepress/public/fonts/JetBrainsMono/woff2/JetBrainsMono-Light-Italic.woff2 diff --git a/site/themes/sogilis/static/fonts/JetBrainsMono/woff2/JetBrainsMono-Light.woff2 b/blog/.vuepress/public/fonts/JetBrainsMono/woff2/JetBrainsMono-Light.woff2 similarity index 100% rename from site/themes/sogilis/static/fonts/JetBrainsMono/woff2/JetBrainsMono-Light.woff2 rename to blog/.vuepress/public/fonts/JetBrainsMono/woff2/JetBrainsMono-Light.woff2 diff --git a/site/themes/sogilis/static/fonts/JetBrainsMono/woff2/JetBrainsMono-Medium-Italic.woff2 b/blog/.vuepress/public/fonts/JetBrainsMono/woff2/JetBrainsMono-Medium-Italic.woff2 similarity index 100% rename from site/themes/sogilis/static/fonts/JetBrainsMono/woff2/JetBrainsMono-Medium-Italic.woff2 rename to blog/.vuepress/public/fonts/JetBrainsMono/woff2/JetBrainsMono-Medium-Italic.woff2 diff --git a/site/themes/sogilis/static/fonts/JetBrainsMono/woff2/JetBrainsMono-Medium.woff2 b/blog/.vuepress/public/fonts/JetBrainsMono/woff2/JetBrainsMono-Medium.woff2 similarity index 100% rename from site/themes/sogilis/static/fonts/JetBrainsMono/woff2/JetBrainsMono-Medium.woff2 rename to blog/.vuepress/public/fonts/JetBrainsMono/woff2/JetBrainsMono-Medium.woff2 diff --git a/site/themes/sogilis/static/fonts/JetBrainsMono/woff2/JetBrainsMono-Regular.woff2 b/blog/.vuepress/public/fonts/JetBrainsMono/woff2/JetBrainsMono-Regular.woff2 similarity index 100% rename from site/themes/sogilis/static/fonts/JetBrainsMono/woff2/JetBrainsMono-Regular.woff2 rename to blog/.vuepress/public/fonts/JetBrainsMono/woff2/JetBrainsMono-Regular.woff2 diff --git a/site/themes/sogilis/static/fonts/JetBrainsMono/woff2/JetBrainsMono-SemiLight-Italic.woff2 b/blog/.vuepress/public/fonts/JetBrainsMono/woff2/JetBrainsMono-SemiLight-Italic.woff2 similarity index 100% rename from site/themes/sogilis/static/fonts/JetBrainsMono/woff2/JetBrainsMono-SemiLight-Italic.woff2 rename to blog/.vuepress/public/fonts/JetBrainsMono/woff2/JetBrainsMono-SemiLight-Italic.woff2 diff --git a/site/themes/sogilis/static/fonts/JetBrainsMono/woff2/JetBrainsMono-SemiLight.woff2 b/blog/.vuepress/public/fonts/JetBrainsMono/woff2/JetBrainsMono-SemiLight.woff2 similarity index 100% rename from site/themes/sogilis/static/fonts/JetBrainsMono/woff2/JetBrainsMono-SemiLight.woff2 rename to blog/.vuepress/public/fonts/JetBrainsMono/woff2/JetBrainsMono-SemiLight.woff2 diff --git a/site/static/img/2013-12-Sogiday-post-it.jpg b/blog/.vuepress/public/img/2013-12-Sogiday-post-it.jpg similarity index 100% rename from site/static/img/2013-12-Sogiday-post-it.jpg rename to blog/.vuepress/public/img/2013-12-Sogiday-post-it.jpg diff --git a/site/static/img/2013-12-Sogilis-Christophe-Levet-Photographe-7326.jpg b/blog/.vuepress/public/img/2013-12-Sogilis-Christophe-Levet-Photographe-7326.jpg similarity index 100% rename from site/static/img/2013-12-Sogilis-Christophe-Levet-Photographe-7326.jpg rename to blog/.vuepress/public/img/2013-12-Sogilis-Christophe-Levet-Photographe-7326.jpg diff --git a/site/static/img/2014-01-Sogilis-Christophe-Levet-Photographe-7914.jpg b/blog/.vuepress/public/img/2014-01-Sogilis-Christophe-Levet-Photographe-7914.jpg similarity index 100% rename from site/static/img/2014-01-Sogilis-Christophe-Levet-Photographe-7914.jpg rename to blog/.vuepress/public/img/2014-01-Sogilis-Christophe-Levet-Photographe-7914.jpg diff --git a/site/static/img/2014-02-Sogilis-Christophe-Levet-Photographe-7596.jpg b/blog/.vuepress/public/img/2014-02-Sogilis-Christophe-Levet-Photographe-7596.jpg similarity index 100% rename from site/static/img/2014-02-Sogilis-Christophe-Levet-Photographe-7596.jpg rename to blog/.vuepress/public/img/2014-02-Sogilis-Christophe-Levet-Photographe-7596.jpg diff --git a/site/static/img/2014-02-fuck_yeah.gif b/blog/.vuepress/public/img/2014-02-fuck_yeah.gif similarity index 100% rename from site/static/img/2014-02-fuck_yeah.gif rename to blog/.vuepress/public/img/2014-02-fuck_yeah.gif diff --git a/site/static/img/2014-02-no_idea.gif b/blog/.vuepress/public/img/2014-02-no_idea.gif similarity index 100% rename from site/static/img/2014-02-no_idea.gif rename to blog/.vuepress/public/img/2014-02-no_idea.gif diff --git a/site/static/img/2014-02-sad.gif b/blog/.vuepress/public/img/2014-02-sad.gif similarity index 100% rename from site/static/img/2014-02-sad.gif rename to blog/.vuepress/public/img/2014-02-sad.gif diff --git a/site/static/img/2014-02-yes.gif b/blog/.vuepress/public/img/2014-02-yes.gif similarity index 100% rename from site/static/img/2014-02-yes.gif rename to blog/.vuepress/public/img/2014-02-yes.gif diff --git a/site/static/img/2014-04-Sogilis-Christophe-Levet-Photographe-7433.jpg b/blog/.vuepress/public/img/2014-04-Sogilis-Christophe-Levet-Photographe-7433.jpg similarity index 100% rename from site/static/img/2014-04-Sogilis-Christophe-Levet-Photographe-7433.jpg rename to blog/.vuepress/public/img/2014-04-Sogilis-Christophe-Levet-Photographe-7433.jpg diff --git a/site/static/img/2014-04-tumblr_inline_n3b2avvoZ21sc5im4.png b/blog/.vuepress/public/img/2014-04-tumblr_inline_n3b2avvoZ21sc5im4.png similarity index 100% rename from site/static/img/2014-04-tumblr_inline_n3b2avvoZ21sc5im4.png rename to blog/.vuepress/public/img/2014-04-tumblr_inline_n3b2avvoZ21sc5im4.png diff --git a/site/static/img/2014-05-Sogilis-Christophe-Levet-Photographe-8389.jpg b/blog/.vuepress/public/img/2014-05-Sogilis-Christophe-Levet-Photographe-8389.jpg similarity index 100% rename from site/static/img/2014-05-Sogilis-Christophe-Levet-Photographe-8389.jpg rename to blog/.vuepress/public/img/2014-05-Sogilis-Christophe-Levet-Photographe-8389.jpg diff --git a/site/static/img/2014-08-Sogilis-Christophe-Levet-Photographe-7517.jpg b/blog/.vuepress/public/img/2014-08-Sogilis-Christophe-Levet-Photographe-7517.jpg similarity index 100% rename from site/static/img/2014-08-Sogilis-Christophe-Levet-Photographe-7517.jpg rename to blog/.vuepress/public/img/2014-08-Sogilis-Christophe-Levet-Photographe-7517.jpg diff --git a/site/static/img/2014-10-Sogilis-Christophe-Levet-Photographe-7651.jpg b/blog/.vuepress/public/img/2014-10-Sogilis-Christophe-Levet-Photographe-7651.jpg similarity index 100% rename from site/static/img/2014-10-Sogilis-Christophe-Levet-Photographe-7651.jpg rename to blog/.vuepress/public/img/2014-10-Sogilis-Christophe-Levet-Photographe-7651.jpg diff --git a/site/static/img/2014-11-Sogilis-Christophe-Levet-Photographe-8877.jpg b/blog/.vuepress/public/img/2014-11-Sogilis-Christophe-Levet-Photographe-8877.jpg similarity index 100% rename from site/static/img/2014-11-Sogilis-Christophe-Levet-Photographe-8877.jpg rename to blog/.vuepress/public/img/2014-11-Sogilis-Christophe-Levet-Photographe-8877.jpg diff --git a/site/static/img/2014-11-tumblr_inline_ncppxsqeBr1sc5im4.png b/blog/.vuepress/public/img/2014-11-tumblr_inline_ncppxsqeBr1sc5im4.png similarity index 100% rename from site/static/img/2014-11-tumblr_inline_ncppxsqeBr1sc5im4.png rename to blog/.vuepress/public/img/2014-11-tumblr_inline_ncppxsqeBr1sc5im4.png diff --git a/site/static/img/2014-11-tumblr_inline_ncppyebVo51sc5im4.png b/blog/.vuepress/public/img/2014-11-tumblr_inline_ncppyebVo51sc5im4.png similarity index 100% rename from site/static/img/2014-11-tumblr_inline_ncppyebVo51sc5im4.png rename to blog/.vuepress/public/img/2014-11-tumblr_inline_ncppyebVo51sc5im4.png diff --git a/site/static/img/2014-11-tumblr_inline_ncppyr3M7h1sc5im4.png b/blog/.vuepress/public/img/2014-11-tumblr_inline_ncppyr3M7h1sc5im4.png similarity index 100% rename from site/static/img/2014-11-tumblr_inline_ncppyr3M7h1sc5im4.png rename to blog/.vuepress/public/img/2014-11-tumblr_inline_ncppyr3M7h1sc5im4.png diff --git a/site/static/img/2014-11-tumblr_inline_ncppz4EuZo1sc5im4.png b/blog/.vuepress/public/img/2014-11-tumblr_inline_ncppz4EuZo1sc5im4.png similarity index 100% rename from site/static/img/2014-11-tumblr_inline_ncppz4EuZo1sc5im4.png rename to blog/.vuepress/public/img/2014-11-tumblr_inline_ncppz4EuZo1sc5im4.png diff --git a/site/static/img/2014-11-tumblr_inline_ncpr1fhc5T1sc5im4.png b/blog/.vuepress/public/img/2014-11-tumblr_inline_ncpr1fhc5T1sc5im4.png similarity index 100% rename from site/static/img/2014-11-tumblr_inline_ncpr1fhc5T1sc5im4.png rename to blog/.vuepress/public/img/2014-11-tumblr_inline_ncpr1fhc5T1sc5im4.png diff --git a/site/static/img/2014-12-tumblr_inline_nez8kcKWWl1sv6muh.png b/blog/.vuepress/public/img/2014-12-tumblr_inline_nez8kcKWWl1sv6muh.png similarity index 100% rename from site/static/img/2014-12-tumblr_inline_nez8kcKWWl1sv6muh.png rename to blog/.vuepress/public/img/2014-12-tumblr_inline_nez8kcKWWl1sv6muh.png diff --git a/site/static/img/2014-12-tumblr_inline_nez8ktrbKd1sv6muh.png b/blog/.vuepress/public/img/2014-12-tumblr_inline_nez8ktrbKd1sv6muh.png similarity index 100% rename from site/static/img/2014-12-tumblr_inline_nez8ktrbKd1sv6muh.png rename to blog/.vuepress/public/img/2014-12-tumblr_inline_nez8ktrbKd1sv6muh.png diff --git a/site/static/img/2014-12-tumblr_inline_nez8l9PRWV1sv6muh.png b/blog/.vuepress/public/img/2014-12-tumblr_inline_nez8l9PRWV1sv6muh.png similarity index 100% rename from site/static/img/2014-12-tumblr_inline_nez8l9PRWV1sv6muh.png rename to blog/.vuepress/public/img/2014-12-tumblr_inline_nez8l9PRWV1sv6muh.png diff --git a/site/static/img/2014-12-tumblr_inline_nez8lrqrff1sv6muh.png b/blog/.vuepress/public/img/2014-12-tumblr_inline_nez8lrqrff1sv6muh.png similarity index 100% rename from site/static/img/2014-12-tumblr_inline_nez8lrqrff1sv6muh.png rename to blog/.vuepress/public/img/2014-12-tumblr_inline_nez8lrqrff1sv6muh.png diff --git a/site/static/img/2014-12-tumblr_inline_nflo1khSyE1sv6muh.png b/blog/.vuepress/public/img/2014-12-tumblr_inline_nflo1khSyE1sv6muh.png similarity index 100% rename from site/static/img/2014-12-tumblr_inline_nflo1khSyE1sv6muh.png rename to blog/.vuepress/public/img/2014-12-tumblr_inline_nflo1khSyE1sv6muh.png diff --git "a/site/static/img/2015-02-J\303\251r\303\251my2.jpg" "b/blog/.vuepress/public/img/2015-02-J\303\251r\303\251my2.jpg" similarity index 100% rename from "site/static/img/2015-02-J\303\251r\303\251my2.jpg" rename to "blog/.vuepress/public/img/2015-02-J\303\251r\303\251my2.jpg" diff --git a/site/static/img/2015-02-capabilities.001-1024x768.png b/blog/.vuepress/public/img/2015-02-capabilities.001-1024x768.png similarity index 100% rename from site/static/img/2015-02-capabilities.001-1024x768.png rename to blog/.vuepress/public/img/2015-02-capabilities.001-1024x768.png diff --git a/site/static/img/2015-02-storymap.jpg b/blog/.vuepress/public/img/2015-02-storymap.jpg similarity index 100% rename from site/static/img/2015-02-storymap.jpg rename to blog/.vuepress/public/img/2015-02-storymap.jpg diff --git a/site/static/img/2015-03-Google-Spreadsheet-Ruby.png b/blog/.vuepress/public/img/2015-03-Google-Spreadsheet-Ruby.png similarity index 100% rename from site/static/img/2015-03-Google-Spreadsheet-Ruby.png rename to blog/.vuepress/public/img/2015-03-Google-Spreadsheet-Ruby.png diff --git "a/site/static/img/2015-03-J\303\251r\303\251my-1.jpg" "b/blog/.vuepress/public/img/2015-03-J\303\251r\303\251my-1.jpg" similarity index 100% rename from "site/static/img/2015-03-J\303\251r\303\251my-1.jpg" rename to "blog/.vuepress/public/img/2015-03-J\303\251r\303\251my-1.jpg" diff --git a/site/static/img/2015-03-Oauth2GoogleDocs.png b/blog/.vuepress/public/img/2015-03-Oauth2GoogleDocs.png similarity index 100% rename from site/static/img/2015-03-Oauth2GoogleDocs.png rename to blog/.vuepress/public/img/2015-03-Oauth2GoogleDocs.png diff --git a/site/static/img/2015-03-Securirty-with-OAuth2.png b/blog/.vuepress/public/img/2015-03-Securirty-with-OAuth2.png similarity index 100% rename from site/static/img/2015-03-Securirty-with-OAuth2.png rename to blog/.vuepress/public/img/2015-03-Securirty-with-OAuth2.png diff --git a/site/static/img/2015-03-Sogilis-Christophe-Levet-Photographe-7461-1024x615.jpg b/blog/.vuepress/public/img/2015-03-Sogilis-Christophe-Levet-Photographe-7461-1024x615.jpg similarity index 100% rename from site/static/img/2015-03-Sogilis-Christophe-Levet-Photographe-7461-1024x615.jpg rename to blog/.vuepress/public/img/2015-03-Sogilis-Christophe-Levet-Photographe-7461-1024x615.jpg diff --git a/site/static/img/2015-03-Sogilis-Christophe-Levet-Photographe-7461.jpg b/blog/.vuepress/public/img/2015-03-Sogilis-Christophe-Levet-Photographe-7461.jpg similarity index 100% rename from site/static/img/2015-03-Sogilis-Christophe-Levet-Photographe-7461.jpg rename to blog/.vuepress/public/img/2015-03-Sogilis-Christophe-Levet-Photographe-7461.jpg diff --git a/site/static/img/2015-03-Sogilis-Christophe-Levet-Photographe-7503.jpg b/blog/.vuepress/public/img/2015-03-Sogilis-Christophe-Levet-Photographe-7503.jpg similarity index 100% rename from site/static/img/2015-03-Sogilis-Christophe-Levet-Photographe-7503.jpg rename to blog/.vuepress/public/img/2015-03-Sogilis-Christophe-Levet-Photographe-7503.jpg diff --git a/site/static/img/2015-05-changeset.png b/blog/.vuepress/public/img/2015-05-changeset.png similarity index 100% rename from site/static/img/2015-05-changeset.png rename to blog/.vuepress/public/img/2015-05-changeset.png diff --git a/site/static/img/2015-05-changeset_applied.png b/blog/.vuepress/public/img/2015-05-changeset_applied.png similarity index 100% rename from site/static/img/2015-05-changeset_applied.png rename to blog/.vuepress/public/img/2015-05-changeset_applied.png diff --git a/site/static/img/2015-05-changeset_conflict.png b/blog/.vuepress/public/img/2015-05-changeset_conflict.png similarity index 100% rename from site/static/img/2015-05-changeset_conflict.png rename to blog/.vuepress/public/img/2015-05-changeset_conflict.png diff --git a/site/static/img/2015-05-cherrypick.gif b/blog/.vuepress/public/img/2015-05-cherrypick.gif similarity index 100% rename from site/static/img/2015-05-cherrypick.gif rename to blog/.vuepress/public/img/2015-05-cherrypick.gif diff --git a/site/static/img/2015-05-gitk_rebase.png b/blog/.vuepress/public/img/2015-05-gitk_rebase.png similarity index 100% rename from site/static/img/2015-05-gitk_rebase.png rename to blog/.vuepress/public/img/2015-05-gitk_rebase.png diff --git a/site/static/img/2015-05-graph.png b/blog/.vuepress/public/img/2015-05-graph.png similarity index 100% rename from site/static/img/2015-05-graph.png rename to blog/.vuepress/public/img/2015-05-graph.png diff --git a/site/static/img/2015-05-graph_branch.png b/blog/.vuepress/public/img/2015-05-graph_branch.png similarity index 100% rename from site/static/img/2015-05-graph_branch.png rename to blog/.vuepress/public/img/2015-05-graph_branch.png diff --git a/site/static/img/2015-05-graph_diff.png b/blog/.vuepress/public/img/2015-05-graph_diff.png similarity index 100% rename from site/static/img/2015-05-graph_diff.png rename to blog/.vuepress/public/img/2015-05-graph_diff.png diff --git a/site/static/img/2015-05-graph_merge.png b/blog/.vuepress/public/img/2015-05-graph_merge.png similarity index 100% rename from site/static/img/2015-05-graph_merge.png rename to blog/.vuepress/public/img/2015-05-graph_merge.png diff --git a/site/static/img/2015-05-merge_ancestors.png b/blog/.vuepress/public/img/2015-05-merge_ancestors.png similarity index 100% rename from site/static/img/2015-05-merge_ancestors.png rename to blog/.vuepress/public/img/2015-05-merge_ancestors.png diff --git a/site/static/img/2015-05-merge_crisscross.png b/blog/.vuepress/public/img/2015-05-merge_crisscross.png similarity index 100% rename from site/static/img/2015-05-merge_crisscross.png rename to blog/.vuepress/public/img/2015-05-merge_crisscross.png diff --git a/site/static/img/2015-05-merge_step1.png b/blog/.vuepress/public/img/2015-05-merge_step1.png similarity index 100% rename from site/static/img/2015-05-merge_step1.png rename to blog/.vuepress/public/img/2015-05-merge_step1.png diff --git a/site/static/img/2015-05-merge_step2.png b/blog/.vuepress/public/img/2015-05-merge_step2.png similarity index 100% rename from site/static/img/2015-05-merge_step2.png rename to blog/.vuepress/public/img/2015-05-merge_step2.png diff --git a/site/static/img/2015-05-rebase.gif b/blog/.vuepress/public/img/2015-05-rebase.gif similarity index 100% rename from site/static/img/2015-05-rebase.gif rename to blog/.vuepress/public/img/2015-05-rebase.gif diff --git a/site/static/img/2015-05-rebase_orphan.png b/blog/.vuepress/public/img/2015-05-rebase_orphan.png similarity index 100% rename from site/static/img/2015-05-rebase_orphan.png rename to blog/.vuepress/public/img/2015-05-rebase_orphan.png diff --git a/site/static/img/2015-05-snapshot.png b/blog/.vuepress/public/img/2015-05-snapshot.png similarity index 100% rename from site/static/img/2015-05-snapshot.png rename to blog/.vuepress/public/img/2015-05-snapshot.png diff --git a/site/static/img/2015-05-working_copy_update.png b/blog/.vuepress/public/img/2015-05-working_copy_update.png similarity index 100% rename from site/static/img/2015-05-working_copy_update.png rename to blog/.vuepress/public/img/2015-05-working_copy_update.png diff --git a/site/static/img/2015-07-Sogilis-Christophe-Levet-Photographe-7533.jpg b/blog/.vuepress/public/img/2015-07-Sogilis-Christophe-Levet-Photographe-7533.jpg similarity index 100% rename from site/static/img/2015-07-Sogilis-Christophe-Levet-Photographe-7533.jpg rename to blog/.vuepress/public/img/2015-07-Sogilis-Christophe-Levet-Photographe-7533.jpg diff --git a/site/static/img/2015-09-Basket.jpg b/blog/.vuepress/public/img/2015-09-Basket.jpg similarity index 100% rename from site/static/img/2015-09-Basket.jpg rename to blog/.vuepress/public/img/2015-09-Basket.jpg diff --git a/site/static/img/2015-09-SogiPeluche.jpg b/blog/.vuepress/public/img/2015-09-SogiPeluche.jpg similarity index 100% rename from site/static/img/2015-09-SogiPeluche.jpg rename to blog/.vuepress/public/img/2015-09-SogiPeluche.jpg diff --git a/site/static/img/2015-10-Sogilis-Christophe-Levet-Photographe-7461-1024x615.jpg b/blog/.vuepress/public/img/2015-10-Sogilis-Christophe-Levet-Photographe-7461-1024x615.jpg similarity index 100% rename from site/static/img/2015-10-Sogilis-Christophe-Levet-Photographe-7461-1024x615.jpg rename to blog/.vuepress/public/img/2015-10-Sogilis-Christophe-Levet-Photographe-7461-1024x615.jpg diff --git a/site/static/img/2015-10-Sogilis-Christophe-Levet-Photographe-7461.jpg b/blog/.vuepress/public/img/2015-10-Sogilis-Christophe-Levet-Photographe-7461.jpg similarity index 100% rename from site/static/img/2015-10-Sogilis-Christophe-Levet-Photographe-7461.jpg rename to blog/.vuepress/public/img/2015-10-Sogilis-Christophe-Levet-Photographe-7461.jpg diff --git a/site/static/img/2015-11-Luc.jpg b/blog/.vuepress/public/img/2015-11-Luc.jpg similarity index 100% rename from site/static/img/2015-11-Luc.jpg rename to blog/.vuepress/public/img/2015-11-Luc.jpg diff --git a/site/static/img/2015-12-Livret.jpg b/blog/.vuepress/public/img/2015-12-Livret.jpg similarity index 100% rename from site/static/img/2015-12-Livret.jpg rename to blog/.vuepress/public/img/2015-12-Livret.jpg diff --git a/site/static/img/2015-12-Sogilis-Christophe-Levet-Photographe-7811.jpg b/blog/.vuepress/public/img/2015-12-Sogilis-Christophe-Levet-Photographe-7811.jpg similarity index 100% rename from site/static/img/2015-12-Sogilis-Christophe-Levet-Photographe-7811.jpg rename to blog/.vuepress/public/img/2015-12-Sogilis-Christophe-Levet-Photographe-7811.jpg diff --git a/site/static/img/2016-02-PasSSII.jpg b/blog/.vuepress/public/img/2016-02-PasSSII.jpg similarity index 100% rename from site/static/img/2016-02-PasSSII.jpg rename to blog/.vuepress/public/img/2016-02-PasSSII.jpg diff --git a/site/static/img/2016-02-Sogilis-Christophe-Levet-Photographe-7803.jpg b/blog/.vuepress/public/img/2016-02-Sogilis-Christophe-Levet-Photographe-7803.jpg similarity index 100% rename from site/static/img/2016-02-Sogilis-Christophe-Levet-Photographe-7803.jpg rename to blog/.vuepress/public/img/2016-02-Sogilis-Christophe-Levet-Photographe-7803.jpg diff --git a/site/static/img/2016-02-calcul-de-risque.png b/blog/.vuepress/public/img/2016-02-calcul-de-risque.png similarity index 100% rename from site/static/img/2016-02-calcul-de-risque.png rename to blog/.vuepress/public/img/2016-02-calcul-de-risque.png diff --git "a/site/static/img/2016-02-composition-de-responsabilit\303\251s.png" "b/blog/.vuepress/public/img/2016-02-composition-de-responsabilit\303\251s.png" similarity index 100% rename from "site/static/img/2016-02-composition-de-responsabilit\303\251s.png" rename to "blog/.vuepress/public/img/2016-02-composition-de-responsabilit\303\251s.png" diff --git a/site/static/img/2016-02-niveau-dapplication-1.png b/blog/.vuepress/public/img/2016-02-niveau-dapplication-1.png similarity index 100% rename from site/static/img/2016-02-niveau-dapplication-1.png rename to blog/.vuepress/public/img/2016-02-niveau-dapplication-1.png diff --git a/site/static/img/2016-04-0.Haut_Page-300x200.jpg b/blog/.vuepress/public/img/2016-04-0.Haut_Page-300x200.jpg similarity index 100% rename from site/static/img/2016-04-0.Haut_Page-300x200.jpg rename to blog/.vuepress/public/img/2016-04-0.Haut_Page-300x200.jpg diff --git a/site/static/img/2016-04-0.Haut_page.jpg b/blog/.vuepress/public/img/2016-04-0.Haut_page.jpg similarity index 100% rename from site/static/img/2016-04-0.Haut_page.jpg rename to blog/.vuepress/public/img/2016-04-0.Haut_page.jpg diff --git a/site/static/img/2016-04-0.Haut_page.png b/blog/.vuepress/public/img/2016-04-0.Haut_page.png similarity index 100% rename from site/static/img/2016-04-0.Haut_page.png rename to blog/.vuepress/public/img/2016-04-0.Haut_page.png diff --git a/site/static/img/2016-04-0.png b/blog/.vuepress/public/img/2016-04-0.png similarity index 100% rename from site/static/img/2016-04-0.png rename to blog/.vuepress/public/img/2016-04-0.png diff --git a/site/static/img/2016-04-1.Design.jpg b/blog/.vuepress/public/img/2016-04-1.Design.jpg similarity index 100% rename from site/static/img/2016-04-1.Design.jpg rename to blog/.vuepress/public/img/2016-04-1.Design.jpg diff --git a/site/static/img/2016-04-1.Developpement-300x200.jpg b/blog/.vuepress/public/img/2016-04-1.Developpement-300x200.jpg similarity index 100% rename from site/static/img/2016-04-1.Developpement-300x200.jpg rename to blog/.vuepress/public/img/2016-04-1.Developpement-300x200.jpg diff --git a/site/static/img/2016-04-1.Formations.jpg b/blog/.vuepress/public/img/2016-04-1.Formations.jpg similarity index 100% rename from site/static/img/2016-04-1.Formations.jpg rename to blog/.vuepress/public/img/2016-04-1.Formations.jpg diff --git a/site/static/img/2016-04-1.GRENOBLE.jpg b/blog/.vuepress/public/img/2016-04-1.GRENOBLE.jpg similarity index 100% rename from site/static/img/2016-04-1.GRENOBLE.jpg rename to blog/.vuepress/public/img/2016-04-1.GRENOBLE.jpg diff --git a/site/static/img/2016-04-1.Notre-vision.jpg b/blog/.vuepress/public/img/2016-04-1.Notre-vision.jpg similarity index 100% rename from site/static/img/2016-04-1.Notre-vision.jpg rename to blog/.vuepress/public/img/2016-04-1.Notre-vision.jpg diff --git a/site/static/img/2016-04-1.Produits.jpg b/blog/.vuepress/public/img/2016-04-1.Produits.jpg similarity index 100% rename from site/static/img/2016-04-1.Produits.jpg rename to blog/.vuepress/public/img/2016-04-1.Produits.jpg diff --git a/site/static/img/2016-04-1equipe-300x163.png b/blog/.vuepress/public/img/2016-04-1equipe-300x163.png similarity index 100% rename from site/static/img/2016-04-1equipe-300x163.png rename to blog/.vuepress/public/img/2016-04-1equipe-300x163.png diff --git a/site/static/img/2016-04-2.CS_BUSINESS_BasPage.jpg b/blog/.vuepress/public/img/2016-04-2.CS_BUSINESS_BasPage.jpg similarity index 100% rename from site/static/img/2016-04-2.CS_BUSINESS_BasPage.jpg rename to blog/.vuepress/public/img/2016-04-2.CS_BUSINESS_BasPage.jpg diff --git a/site/static/img/2016-04-2.Developpement-1.jpg b/blog/.vuepress/public/img/2016-04-2.Developpement-1.jpg similarity index 100% rename from site/static/img/2016-04-2.Developpement-1.jpg rename to blog/.vuepress/public/img/2016-04-2.Developpement-1.jpg diff --git a/site/static/img/2016-04-2.Developpement-e1460448074336.jpg b/blog/.vuepress/public/img/2016-04-2.Developpement-e1460448074336.jpg similarity index 100% rename from site/static/img/2016-04-2.Developpement-e1460448074336.jpg rename to blog/.vuepress/public/img/2016-04-2.Developpement-e1460448074336.jpg diff --git a/site/static/img/2016-04-2.Formations-e1461591900149.jpg b/blog/.vuepress/public/img/2016-04-2.Formations-e1461591900149.jpg similarity index 100% rename from site/static/img/2016-04-2.Formations-e1461591900149.jpg rename to blog/.vuepress/public/img/2016-04-2.Formations-e1461591900149.jpg diff --git a/site/static/img/2016-04-2.La-vie-a-Sogilis.jpg b/blog/.vuepress/public/img/2016-04-2.La-vie-a-Sogilis.jpg similarity index 100% rename from site/static/img/2016-04-2.La-vie-a-Sogilis.jpg rename to blog/.vuepress/public/img/2016-04-2.La-vie-a-Sogilis.jpg diff --git a/site/static/img/2016-04-2.Notre-vision.jpg b/blog/.vuepress/public/img/2016-04-2.Notre-vision.jpg similarity index 100% rename from site/static/img/2016-04-2.Notre-vision.jpg rename to blog/.vuepress/public/img/2016-04-2.Notre-vision.jpg diff --git a/site/static/img/2016-04-2.On-simplique.jpg b/blog/.vuepress/public/img/2016-04-2.On-simplique.jpg similarity index 100% rename from site/static/img/2016-04-2.On-simplique.jpg rename to blog/.vuepress/public/img/2016-04-2.On-simplique.jpg diff --git a/site/static/img/2016-04-2.Produits.jpg b/blog/.vuepress/public/img/2016-04-2.Produits.jpg similarity index 100% rename from site/static/img/2016-04-2.Produits.jpg rename to blog/.vuepress/public/img/2016-04-2.Produits.jpg diff --git a/site/static/img/2016-04-3.CS_BUSINESS_BasPage-300x200.jpg b/blog/.vuepress/public/img/2016-04-3.CS_BUSINESS_BasPage-300x200.jpg similarity index 100% rename from site/static/img/2016-04-3.CS_BUSINESS_BasPage-300x200.jpg rename to blog/.vuepress/public/img/2016-04-3.CS_BUSINESS_BasPage-300x200.jpg diff --git a/site/static/img/2016-04-3.CS_BUSINESS_BasPage.jpg b/blog/.vuepress/public/img/2016-04-3.CS_BUSINESS_BasPage.jpg similarity index 100% rename from site/static/img/2016-04-3.CS_BUSINESS_BasPage.jpg rename to blog/.vuepress/public/img/2016-04-3.CS_BUSINESS_BasPage.jpg diff --git a/site/static/img/2016-04-5.png b/blog/.vuepress/public/img/2016-04-5.png similarity index 100% rename from site/static/img/2016-04-5.png rename to blog/.vuepress/public/img/2016-04-5.png diff --git a/site/static/img/2016-04-Sogilis-Christophe-Levet-Photographe-7898.jpg b/blog/.vuepress/public/img/2016-04-Sogilis-Christophe-Levet-Photographe-7898.jpg similarity index 100% rename from site/static/img/2016-04-Sogilis-Christophe-Levet-Photographe-7898.jpg rename to blog/.vuepress/public/img/2016-04-Sogilis-Christophe-Levet-Photographe-7898.jpg diff --git a/site/static/img/2016-04-gre.png b/blog/.vuepress/public/img/2016-04-gre.png similarity index 100% rename from site/static/img/2016-04-gre.png rename to blog/.vuepress/public/img/2016-04-gre.png diff --git a/site/static/img/2016-05-0.png b/blog/.vuepress/public/img/2016-05-0.png similarity index 100% rename from site/static/img/2016-05-0.png rename to blog/.vuepress/public/img/2016-05-0.png diff --git a/site/static/img/2016-05-1.png b/blog/.vuepress/public/img/2016-05-1.png similarity index 100% rename from site/static/img/2016-05-1.png rename to blog/.vuepress/public/img/2016-05-1.png diff --git a/site/static/img/2016-05-2.png b/blog/.vuepress/public/img/2016-05-2.png similarity index 100% rename from site/static/img/2016-05-2.png rename to blog/.vuepress/public/img/2016-05-2.png diff --git a/site/static/img/2016-05-IMG_0527.png b/blog/.vuepress/public/img/2016-05-IMG_0527.png similarity index 100% rename from site/static/img/2016-05-IMG_0527.png rename to blog/.vuepress/public/img/2016-05-IMG_0527.png diff --git a/site/static/img/2016-05-Sogilis-Christophe-Levet-Photographe-7749-300x200.jpg b/blog/.vuepress/public/img/2016-05-Sogilis-Christophe-Levet-Photographe-7749-300x200.jpg similarity index 100% rename from site/static/img/2016-05-Sogilis-Christophe-Levet-Photographe-7749-300x200.jpg rename to blog/.vuepress/public/img/2016-05-Sogilis-Christophe-Levet-Photographe-7749-300x200.jpg diff --git a/site/static/img/2016-05-Sogilis-Christophe-Levet-Photographe-7991-300x141.jpg b/blog/.vuepress/public/img/2016-05-Sogilis-Christophe-Levet-Photographe-7991-300x141.jpg similarity index 100% rename from site/static/img/2016-05-Sogilis-Christophe-Levet-Photographe-7991-300x141.jpg rename to blog/.vuepress/public/img/2016-05-Sogilis-Christophe-Levet-Photographe-7991-300x141.jpg diff --git a/site/static/img/2016-05-Sogilis-Christophe-Levet-Photographe-8212-300x168.jpg b/blog/.vuepress/public/img/2016-05-Sogilis-Christophe-Levet-Photographe-8212-300x168.jpg similarity index 100% rename from site/static/img/2016-05-Sogilis-Christophe-Levet-Photographe-8212-300x168.jpg rename to blog/.vuepress/public/img/2016-05-Sogilis-Christophe-Levet-Photographe-8212-300x168.jpg diff --git a/site/static/img/2016-05-Sogilis-Christophe-Levet-Photographe-8218.jpg b/blog/.vuepress/public/img/2016-05-Sogilis-Christophe-Levet-Photographe-8218.jpg similarity index 100% rename from site/static/img/2016-05-Sogilis-Christophe-Levet-Photographe-8218.jpg rename to blog/.vuepress/public/img/2016-05-Sogilis-Christophe-Levet-Photographe-8218.jpg diff --git a/site/static/img/2016-05-Sogilis-Christophe-Levet-Photographe-8314-300x210.jpg b/blog/.vuepress/public/img/2016-05-Sogilis-Christophe-Levet-Photographe-8314-300x210.jpg similarity index 100% rename from site/static/img/2016-05-Sogilis-Christophe-Levet-Photographe-8314-300x210.jpg rename to blog/.vuepress/public/img/2016-05-Sogilis-Christophe-Levet-Photographe-8314-300x210.jpg diff --git a/site/static/img/2016-05-Sogilis-Christophe-Levet-Photographe-8824-e1462795824232.jpg b/blog/.vuepress/public/img/2016-05-Sogilis-Christophe-Levet-Photographe-8824-e1462795824232.jpg similarity index 100% rename from site/static/img/2016-05-Sogilis-Christophe-Levet-Photographe-8824-e1462795824232.jpg rename to blog/.vuepress/public/img/2016-05-Sogilis-Christophe-Levet-Photographe-8824-e1462795824232.jpg diff --git a/site/static/img/2016-05-Sogilis-Christophe-Levet-Photographe-9104-300x200.jpg b/blog/.vuepress/public/img/2016-05-Sogilis-Christophe-Levet-Photographe-9104-300x200.jpg similarity index 100% rename from site/static/img/2016-05-Sogilis-Christophe-Levet-Photographe-9104-300x200.jpg rename to blog/.vuepress/public/img/2016-05-Sogilis-Christophe-Levet-Photographe-9104-300x200.jpg diff --git a/site/static/img/2016-05-Sogilis-produits.png b/blog/.vuepress/public/img/2016-05-Sogilis-produits.png similarity index 100% rename from site/static/img/2016-05-Sogilis-produits.png rename to blog/.vuepress/public/img/2016-05-Sogilis-produits.png diff --git a/site/static/img/2016-06-20162906_Accompagnement_BMD-300x300.jpg b/blog/.vuepress/public/img/2016-06-20162906_Accompagnement_BMD-300x300.jpg similarity index 100% rename from site/static/img/2016-06-20162906_Accompagnement_BMD-300x300.jpg rename to blog/.vuepress/public/img/2016-06-20162906_Accompagnement_BMD-300x300.jpg diff --git a/site/static/img/2016-06-Article-Simon.jpg b/blog/.vuepress/public/img/2016-06-Article-Simon.jpg similarity index 100% rename from site/static/img/2016-06-Article-Simon.jpg rename to blog/.vuepress/public/img/2016-06-Article-Simon.jpg diff --git a/site/static/img/2016-06-Belle-histoire-favi-201x300.jpg b/blog/.vuepress/public/img/2016-06-Belle-histoire-favi-201x300.jpg similarity index 100% rename from site/static/img/2016-06-Belle-histoire-favi-201x300.jpg rename to blog/.vuepress/public/img/2016-06-Belle-histoire-favi-201x300.jpg diff --git a/site/static/img/2016-06-Designing-Hexo-Mobile-App-1-1.jpg b/blog/.vuepress/public/img/2016-06-Designing-Hexo-Mobile-App-1-1.jpg similarity index 100% rename from site/static/img/2016-06-Designing-Hexo-Mobile-App-1-1.jpg rename to blog/.vuepress/public/img/2016-06-Designing-Hexo-Mobile-App-1-1.jpg diff --git a/site/static/img/2016-06-Designing-Hexo-Mobile-App-10.jpg b/blog/.vuepress/public/img/2016-06-Designing-Hexo-Mobile-App-10.jpg similarity index 100% rename from site/static/img/2016-06-Designing-Hexo-Mobile-App-10.jpg rename to blog/.vuepress/public/img/2016-06-Designing-Hexo-Mobile-App-10.jpg diff --git a/site/static/img/2016-06-Designing-Hexo-Mobile-App-2.png b/blog/.vuepress/public/img/2016-06-Designing-Hexo-Mobile-App-2.png similarity index 100% rename from site/static/img/2016-06-Designing-Hexo-Mobile-App-2.png rename to blog/.vuepress/public/img/2016-06-Designing-Hexo-Mobile-App-2.png diff --git a/site/static/img/2016-06-Designing-Hexo-Mobile-App-3.jpg b/blog/.vuepress/public/img/2016-06-Designing-Hexo-Mobile-App-3.jpg similarity index 100% rename from site/static/img/2016-06-Designing-Hexo-Mobile-App-3.jpg rename to blog/.vuepress/public/img/2016-06-Designing-Hexo-Mobile-App-3.jpg diff --git a/site/static/img/2016-06-Designing-Hexo-Mobile-App-4.jpg b/blog/.vuepress/public/img/2016-06-Designing-Hexo-Mobile-App-4.jpg similarity index 100% rename from site/static/img/2016-06-Designing-Hexo-Mobile-App-4.jpg rename to blog/.vuepress/public/img/2016-06-Designing-Hexo-Mobile-App-4.jpg diff --git a/site/static/img/2016-06-Designing-Hexo-Mobile-App-5.jpg b/blog/.vuepress/public/img/2016-06-Designing-Hexo-Mobile-App-5.jpg similarity index 100% rename from site/static/img/2016-06-Designing-Hexo-Mobile-App-5.jpg rename to blog/.vuepress/public/img/2016-06-Designing-Hexo-Mobile-App-5.jpg diff --git a/site/static/img/2016-06-Designing-Hexo-Mobile-App-6.png b/blog/.vuepress/public/img/2016-06-Designing-Hexo-Mobile-App-6.png similarity index 100% rename from site/static/img/2016-06-Designing-Hexo-Mobile-App-6.png rename to blog/.vuepress/public/img/2016-06-Designing-Hexo-Mobile-App-6.png diff --git a/site/static/img/2016-06-Designing-Hexo-Mobile-App-7.jpg b/blog/.vuepress/public/img/2016-06-Designing-Hexo-Mobile-App-7.jpg similarity index 100% rename from site/static/img/2016-06-Designing-Hexo-Mobile-App-7.jpg rename to blog/.vuepress/public/img/2016-06-Designing-Hexo-Mobile-App-7.jpg diff --git a/site/static/img/2016-06-Designing-Hexo-Mobile-App-8.jpg b/blog/.vuepress/public/img/2016-06-Designing-Hexo-Mobile-App-8.jpg similarity index 100% rename from site/static/img/2016-06-Designing-Hexo-Mobile-App-8.jpg rename to blog/.vuepress/public/img/2016-06-Designing-Hexo-Mobile-App-8.jpg diff --git a/site/static/img/2016-06-Designing-Hexo-Mobile-App-9.jpg b/blog/.vuepress/public/img/2016-06-Designing-Hexo-Mobile-App-9.jpg similarity index 100% rename from site/static/img/2016-06-Designing-Hexo-Mobile-App-9.jpg rename to blog/.vuepress/public/img/2016-06-Designing-Hexo-Mobile-App-9.jpg diff --git a/site/static/img/2016-06-Hexo-NB.jpg b/blog/.vuepress/public/img/2016-06-Hexo-NB.jpg similarity index 100% rename from site/static/img/2016-06-Hexo-NB.jpg rename to blog/.vuepress/public/img/2016-06-Hexo-NB.jpg diff --git a/site/static/img/2016-06-Hexo-article-1.jpg b/blog/.vuepress/public/img/2016-06-Hexo-article-1.jpg similarity index 100% rename from site/static/img/2016-06-Hexo-article-1.jpg rename to blog/.vuepress/public/img/2016-06-Hexo-article-1.jpg diff --git a/site/static/img/2016-06-Laloux-300x207.jpg b/blog/.vuepress/public/img/2016-06-Laloux-300x207.jpg similarity index 100% rename from site/static/img/2016-06-Laloux-300x207.jpg rename to blog/.vuepress/public/img/2016-06-Laloux-300x207.jpg diff --git a/site/static/img/2016-06-Moneymoneymoney.jpg b/blog/.vuepress/public/img/2016-06-Moneymoneymoney.jpg similarity index 100% rename from site/static/img/2016-06-Moneymoneymoney.jpg rename to blog/.vuepress/public/img/2016-06-Moneymoneymoney.jpg diff --git a/site/static/img/2016-06-Zobrist-300x169.jpg b/blog/.vuepress/public/img/2016-06-Zobrist-300x169.jpg similarity index 100% rename from site/static/img/2016-06-Zobrist-300x169.jpg rename to blog/.vuepress/public/img/2016-06-Zobrist-300x169.jpg diff --git a/site/static/img/2016-07-1.01-Camera-Movements.png b/blog/.vuepress/public/img/2016-07-1.01-Camera-Movements.png similarity index 100% rename from site/static/img/2016-07-1.01-Camera-Movements.png rename to blog/.vuepress/public/img/2016-07-1.01-Camera-Movements.png diff --git a/site/static/img/2016-07-CameraFollowBlack.jpg b/blog/.vuepress/public/img/2016-07-CameraFollowBlack.jpg similarity index 100% rename from site/static/img/2016-07-CameraFollowBlack.jpg rename to blog/.vuepress/public/img/2016-07-CameraFollowBlack.jpg diff --git a/site/static/img/2016-07-Camerafollow.png b/blog/.vuepress/public/img/2016-07-Camerafollow.png similarity index 100% rename from site/static/img/2016-07-Camerafollow.png rename to blog/.vuepress/public/img/2016-07-Camerafollow.png diff --git a/site/static/img/2016-07-Recrutement4.png b/blog/.vuepress/public/img/2016-07-Recrutement4.png similarity index 100% rename from site/static/img/2016-07-Recrutement4.png rename to blog/.vuepress/public/img/2016-07-Recrutement4.png diff --git a/site/static/img/2016-07-Sogilis-Christophe-Levet-Photographe-7971.jpg b/blog/.vuepress/public/img/2016-07-Sogilis-Christophe-Levet-Photographe-7971.jpg similarity index 100% rename from site/static/img/2016-07-Sogilis-Christophe-Levet-Photographe-7971.jpg rename to blog/.vuepress/public/img/2016-07-Sogilis-Christophe-Levet-Photographe-7971.jpg diff --git a/site/static/img/2016-07-mouvements.jpg b/blog/.vuepress/public/img/2016-07-mouvements.jpg similarity index 100% rename from site/static/img/2016-07-mouvements.jpg rename to blog/.vuepress/public/img/2016-07-mouvements.jpg diff --git a/site/static/img/2016-07-pfc-android-1.jpg b/blog/.vuepress/public/img/2016-07-pfc-android-1.jpg similarity index 100% rename from site/static/img/2016-07-pfc-android-1.jpg rename to blog/.vuepress/public/img/2016-07-pfc-android-1.jpg diff --git a/site/static/img/2016-07-pfc-android-2.jpg b/blog/.vuepress/public/img/2016-07-pfc-android-2.jpg similarity index 100% rename from site/static/img/2016-07-pfc-android-2.jpg rename to blog/.vuepress/public/img/2016-07-pfc-android-2.jpg diff --git a/site/static/img/2016-07-pfc-ios-1.jpg b/blog/.vuepress/public/img/2016-07-pfc-ios-1.jpg similarity index 100% rename from site/static/img/2016-07-pfc-ios-1.jpg rename to blog/.vuepress/public/img/2016-07-pfc-ios-1.jpg diff --git a/site/static/img/2016-07-pfc-ios-2.jpg b/blog/.vuepress/public/img/2016-07-pfc-ios-2.jpg similarity index 100% rename from site/static/img/2016-07-pfc-ios-2.jpg rename to blog/.vuepress/public/img/2016-07-pfc-ios-2.jpg diff --git a/site/static/img/2016-07-test-chamrousse-min.jpg b/blog/.vuepress/public/img/2016-07-test-chamrousse-min.jpg similarity index 100% rename from site/static/img/2016-07-test-chamrousse-min.jpg rename to blog/.vuepress/public/img/2016-07-test-chamrousse-min.jpg diff --git a/site/static/img/2016-07-testing.gif b/blog/.vuepress/public/img/2016-07-testing.gif similarity index 100% rename from site/static/img/2016-07-testing.gif rename to blog/.vuepress/public/img/2016-07-testing.gif diff --git a/site/static/img/2016-07-xavproto_1-min-BW-min.jpg b/blog/.vuepress/public/img/2016-07-xavproto_1-min-BW-min.jpg similarity index 100% rename from site/static/img/2016-07-xavproto_1-min-BW-min.jpg rename to blog/.vuepress/public/img/2016-07-xavproto_1-min-BW-min.jpg diff --git a/site/static/img/2016-07-xavproto_1-min.jpg b/blog/.vuepress/public/img/2016-07-xavproto_1-min.jpg similarity index 100% rename from site/static/img/2016-07-xavproto_1-min.jpg rename to blog/.vuepress/public/img/2016-07-xavproto_1-min.jpg diff --git "a/site/static/img/2016-10-T\303\252tes-one.png" "b/blog/.vuepress/public/img/2016-10-T\303\252tes-one.png" similarity index 100% rename from "site/static/img/2016-10-T\303\252tes-one.png" rename to "blog/.vuepress/public/img/2016-10-T\303\252tes-one.png" diff --git "a/site/static/img/2016-10-T\303\252tes-three.png" "b/blog/.vuepress/public/img/2016-10-T\303\252tes-three.png" similarity index 100% rename from "site/static/img/2016-10-T\303\252tes-three.png" rename to "blog/.vuepress/public/img/2016-10-T\303\252tes-three.png" diff --git "a/site/static/img/2016-10-T\303\252tes-two.png" "b/blog/.vuepress/public/img/2016-10-T\303\252tes-two.png" similarity index 100% rename from "site/static/img/2016-10-T\303\252tes-two.png" rename to "blog/.vuepress/public/img/2016-10-T\303\252tes-two.png" diff --git a/site/static/img/2016-10-data-representation.png b/blog/.vuepress/public/img/2016-10-data-representation.png similarity index 100% rename from site/static/img/2016-10-data-representation.png rename to blog/.vuepress/public/img/2016-10-data-representation.png diff --git a/site/static/img/2016-10-dimensionality-reduction.png b/blog/.vuepress/public/img/2016-10-dimensionality-reduction.png similarity index 100% rename from site/static/img/2016-10-dimensionality-reduction.png rename to blog/.vuepress/public/img/2016-10-dimensionality-reduction.png diff --git "a/site/static/img/2016-12-Capture-d\342\200\231\303\251cran-2016-12-02-\303\240-18.07.05-1024x560.png" "b/blog/.vuepress/public/img/2016-12-Capture-d\342\200\231\303\251cran-2016-12-02-\303\240-18.07.05-1024x560.png" similarity index 100% rename from "site/static/img/2016-12-Capture-d\342\200\231\303\251cran-2016-12-02-\303\240-18.07.05-1024x560.png" rename to "blog/.vuepress/public/img/2016-12-Capture-d\342\200\231\303\251cran-2016-12-02-\303\240-18.07.05-1024x560.png" diff --git "a/site/static/img/2016-12-Capture-d\342\200\231\303\251cran-2016-12-02-\303\240-18.21.39-1024x592.png" "b/blog/.vuepress/public/img/2016-12-Capture-d\342\200\231\303\251cran-2016-12-02-\303\240-18.21.39-1024x592.png" similarity index 100% rename from "site/static/img/2016-12-Capture-d\342\200\231\303\251cran-2016-12-02-\303\240-18.21.39-1024x592.png" rename to "blog/.vuepress/public/img/2016-12-Capture-d\342\200\231\303\251cran-2016-12-02-\303\240-18.21.39-1024x592.png" diff --git a/site/static/img/2017-01-SonarQube-Projects-1024x225.jpg b/blog/.vuepress/public/img/2017-01-SonarQube-Projects-1024x225.jpg similarity index 100% rename from site/static/img/2017-01-SonarQube-Projects-1024x225.jpg rename to blog/.vuepress/public/img/2017-01-SonarQube-Projects-1024x225.jpg diff --git a/site/static/img/2017-01-SonarQube-Projects2-1024x165.jpg b/blog/.vuepress/public/img/2017-01-SonarQube-Projects2-1024x165.jpg similarity index 100% rename from site/static/img/2017-01-SonarQube-Projects2-1024x165.jpg rename to blog/.vuepress/public/img/2017-01-SonarQube-Projects2-1024x165.jpg diff --git a/site/static/img/2017-01-SonarQube.jpg b/blog/.vuepress/public/img/2017-01-SonarQube.jpg similarity index 100% rename from site/static/img/2017-01-SonarQube.jpg rename to blog/.vuepress/public/img/2017-01-SonarQube.jpg diff --git a/site/static/img/2017-02-Github_inline_comment.png b/blog/.vuepress/public/img/2017-02-Github_inline_comment.png similarity index 100% rename from site/static/img/2017-02-Github_inline_comment.png rename to blog/.vuepress/public/img/2017-02-Github_inline_comment.png diff --git a/site/static/img/2017-02-Github_show_outdated.png b/blog/.vuepress/public/img/2017-02-Github_show_outdated.png similarity index 100% rename from site/static/img/2017-02-Github_show_outdated.png rename to blog/.vuepress/public/img/2017-02-Github_show_outdated.png diff --git a/site/static/img/2017-02-Logo_Sogilis_ecran.jpg b/blog/.vuepress/public/img/2017-02-Logo_Sogilis_ecran.jpg similarity index 100% rename from site/static/img/2017-02-Logo_Sogilis_ecran.jpg rename to blog/.vuepress/public/img/2017-02-Logo_Sogilis_ecran.jpg diff --git a/site/static/img/2017-02-Matching_columns-2.png b/blog/.vuepress/public/img/2017-02-Matching_columns-2.png similarity index 100% rename from site/static/img/2017-02-Matching_columns-2.png rename to blog/.vuepress/public/img/2017-02-Matching_columns-2.png diff --git a/site/static/img/2017-02-Matching_columns.png b/blog/.vuepress/public/img/2017-02-Matching_columns.png similarity index 100% rename from site/static/img/2017-02-Matching_columns.png rename to blog/.vuepress/public/img/2017-02-Matching_columns.png diff --git a/site/static/img/2017-02-Matching_update.png b/blog/.vuepress/public/img/2017-02-Matching_update.png similarity index 100% rename from site/static/img/2017-02-Matching_update.png rename to blog/.vuepress/public/img/2017-02-Matching_update.png diff --git a/site/static/img/2017-02-PR_after_update.png b/blog/.vuepress/public/img/2017-02-PR_after_update.png similarity index 100% rename from site/static/img/2017-02-PR_after_update.png rename to blog/.vuepress/public/img/2017-02-PR_after_update.png diff --git a/site/static/img/2017-02-PR_before_update.png b/blog/.vuepress/public/img/2017-02-PR_before_update.png similarity index 100% rename from site/static/img/2017-02-PR_before_update.png rename to blog/.vuepress/public/img/2017-02-PR_before_update.png diff --git a/site/static/img/2017-02-PR_update.png b/blog/.vuepress/public/img/2017-02-PR_update.png similarity index 100% rename from site/static/img/2017-02-PR_update.png rename to blog/.vuepress/public/img/2017-02-PR_update.png diff --git a/site/static/img/2017-02-Rebase.png b/blog/.vuepress/public/img/2017-02-Rebase.png similarity index 100% rename from site/static/img/2017-02-Rebase.png rename to blog/.vuepress/public/img/2017-02-Rebase.png diff --git a/site/static/img/2017-02-Rebase_naive.png b/blog/.vuepress/public/img/2017-02-Rebase_naive.png similarity index 100% rename from site/static/img/2017-02-Rebase_naive.png rename to blog/.vuepress/public/img/2017-02-Rebase_naive.png diff --git a/site/static/img/2017-02-Rebase_ok.png b/blog/.vuepress/public/img/2017-02-Rebase_ok.png similarity index 100% rename from site/static/img/2017-02-Rebase_ok.png rename to blog/.vuepress/public/img/2017-02-Rebase_ok.png diff --git a/site/static/img/2017-02-Update_vs_rebase.png b/blog/.vuepress/public/img/2017-02-Update_vs_rebase.png similarity index 100% rename from site/static/img/2017-02-Update_vs_rebase.png rename to blog/.vuepress/public/img/2017-02-Update_vs_rebase.png diff --git a/site/static/img/2017-02-logo-clotoo.png b/blog/.vuepress/public/img/2017-02-logo-clotoo.png similarity index 100% rename from site/static/img/2017-02-logo-clotoo.png rename to blog/.vuepress/public/img/2017-02-logo-clotoo.png diff --git a/site/static/img/2017-03-1.png b/blog/.vuepress/public/img/2017-03-1.png similarity index 100% rename from site/static/img/2017-03-1.png rename to blog/.vuepress/public/img/2017-03-1.png diff --git a/site/static/img/2017-03-2.png b/blog/.vuepress/public/img/2017-03-2.png similarity index 100% rename from site/static/img/2017-03-2.png rename to blog/.vuepress/public/img/2017-03-2.png diff --git a/site/static/img/2017-03-Capture-2017-03-02-09.45.32.png b/blog/.vuepress/public/img/2017-03-Capture-2017-03-02-09.45.32.png similarity index 100% rename from site/static/img/2017-03-Capture-2017-03-02-09.45.32.png rename to blog/.vuepress/public/img/2017-03-Capture-2017-03-02-09.45.32.png diff --git a/site/static/img/2017-03-Capture-2017-03-27-15.42.58.png b/blog/.vuepress/public/img/2017-03-Capture-2017-03-27-15.42.58.png similarity index 100% rename from site/static/img/2017-03-Capture-2017-03-27-15.42.58.png rename to blog/.vuepress/public/img/2017-03-Capture-2017-03-27-15.42.58.png diff --git a/site/static/img/2017-03-article_clotoo3.jpg b/blog/.vuepress/public/img/2017-03-article_clotoo3.jpg similarity index 100% rename from site/static/img/2017-03-article_clotoo3.jpg rename to blog/.vuepress/public/img/2017-03-article_clotoo3.jpg diff --git a/site/static/img/2017-04-08VNaLeHA178YtKWYKSHQe3F-e1491924480162-1024x641.jpg b/blog/.vuepress/public/img/2017-04-08VNaLeHA178YtKWYKSHQe3F-e1491924480162-1024x641.jpg similarity index 100% rename from site/static/img/2017-04-08VNaLeHA178YtKWYKSHQe3F-e1491924480162-1024x641.jpg rename to blog/.vuepress/public/img/2017-04-08VNaLeHA178YtKWYKSHQe3F-e1491924480162-1024x641.jpg diff --git a/site/static/img/2017-04-BXK36eAVfHS8KA9SAUFLCd5U-1024x625.jpg b/blog/.vuepress/public/img/2017-04-BXK36eAVfHS8KA9SAUFLCd5U-1024x625.jpg similarity index 100% rename from site/static/img/2017-04-BXK36eAVfHS8KA9SAUFLCd5U-1024x625.jpg rename to blog/.vuepress/public/img/2017-04-BXK36eAVfHS8KA9SAUFLCd5U-1024x625.jpg diff --git a/site/static/img/2017-04-C8p3Q9kW0AE7_07.jpglarge-1024x684.jpeg b/blog/.vuepress/public/img/2017-04-C8p3Q9kW0AE7_07.jpglarge-1024x684.jpeg similarity index 100% rename from site/static/img/2017-04-C8p3Q9kW0AE7_07.jpglarge-1024x684.jpeg rename to blog/.vuepress/public/img/2017-04-C8p3Q9kW0AE7_07.jpglarge-1024x684.jpeg diff --git a/site/static/img/2017-04-C8qSv9rXUAEmA1e.jpglarge-1024x653.jpeg b/blog/.vuepress/public/img/2017-04-C8qSv9rXUAEmA1e.jpglarge-1024x653.jpeg similarity index 100% rename from site/static/img/2017-04-C8qSv9rXUAEmA1e.jpglarge-1024x653.jpeg rename to blog/.vuepress/public/img/2017-04-C8qSv9rXUAEmA1e.jpglarge-1024x653.jpeg diff --git a/site/static/img/2017-04-C8t_dApWAAAkDAp.jpglarge-1024x700.jpeg b/blog/.vuepress/public/img/2017-04-C8t_dApWAAAkDAp.jpglarge-1024x700.jpeg similarity index 100% rename from site/static/img/2017-04-C8t_dApWAAAkDAp.jpglarge-1024x700.jpeg rename to blog/.vuepress/public/img/2017-04-C8t_dApWAAAkDAp.jpglarge-1024x700.jpeg diff --git a/site/static/img/2017-04-C8uwUCxXkAENpcz.jpglarge-1024x635.jpeg b/blog/.vuepress/public/img/2017-04-C8uwUCxXkAENpcz.jpglarge-1024x635.jpeg similarity index 100% rename from site/static/img/2017-04-C8uwUCxXkAENpcz.jpglarge-1024x635.jpeg rename to blog/.vuepress/public/img/2017-04-C8uwUCxXkAENpcz.jpglarge-1024x635.jpeg diff --git a/site/static/img/2017-04-LU4Y5Dda6TYfAfDyTaM9Y1T1-1024x605.jpg b/blog/.vuepress/public/img/2017-04-LU4Y5Dda6TYfAfDyTaM9Y1T1-1024x605.jpg similarity index 100% rename from site/static/img/2017-04-LU4Y5Dda6TYfAfDyTaM9Y1T1-1024x605.jpg rename to blog/.vuepress/public/img/2017-04-LU4Y5Dda6TYfAfDyTaM9Y1T1-1024x605.jpg diff --git a/site/static/img/2017-04-SiDO-et-industrie-du-futur.jpg b/blog/.vuepress/public/img/2017-04-SiDO-et-industrie-du-futur.jpg similarity index 100% rename from site/static/img/2017-04-SiDO-et-industrie-du-futur.jpg rename to blog/.vuepress/public/img/2017-04-SiDO-et-industrie-du-futur.jpg diff --git a/site/static/img/2017-04-YEW9Jbbe352gJAWChD40T7fV-e1491924306419-1024x618.jpg b/blog/.vuepress/public/img/2017-04-YEW9Jbbe352gJAWChD40T7fV-e1491924306419-1024x618.jpg similarity index 100% rename from site/static/img/2017-04-YEW9Jbbe352gJAWChD40T7fV-e1491924306419-1024x618.jpg rename to blog/.vuepress/public/img/2017-04-YEW9Jbbe352gJAWChD40T7fV-e1491924306419-1024x618.jpg diff --git a/site/static/img/2017-05-IMG_3938-2-300x203.jpg b/blog/.vuepress/public/img/2017-05-IMG_3938-2-300x203.jpg similarity index 100% rename from site/static/img/2017-05-IMG_3938-2-300x203.jpg rename to blog/.vuepress/public/img/2017-05-IMG_3938-2-300x203.jpg diff --git a/site/static/img/2017-05-IMG_3942-4b-203x300.jpg b/blog/.vuepress/public/img/2017-05-IMG_3942-4b-203x300.jpg similarity index 100% rename from site/static/img/2017-05-IMG_3942-4b-203x300.jpg rename to blog/.vuepress/public/img/2017-05-IMG_3942-4b-203x300.jpg diff --git a/site/static/img/2017-05-PrtScr-capture.png b/blog/.vuepress/public/img/2017-05-PrtScr-capture.png similarity index 100% rename from site/static/img/2017-05-PrtScr-capture.png rename to blog/.vuepress/public/img/2017-05-PrtScr-capture.png diff --git a/site/static/img/2017-05-Sogilis-Christophe-Levet-Photographe-7461-1024x615.jpg b/blog/.vuepress/public/img/2017-05-Sogilis-Christophe-Levet-Photographe-7461-1024x615.jpg similarity index 100% rename from site/static/img/2017-05-Sogilis-Christophe-Levet-Photographe-7461-1024x615.jpg rename to blog/.vuepress/public/img/2017-05-Sogilis-Christophe-Levet-Photographe-7461-1024x615.jpg diff --git a/site/static/img/2017-05-Sogilis-Christophe-Levet-Photographe-7461.jpg b/blog/.vuepress/public/img/2017-05-Sogilis-Christophe-Levet-Photographe-7461.jpg similarity index 100% rename from site/static/img/2017-05-Sogilis-Christophe-Levet-Photographe-7461.jpg rename to blog/.vuepress/public/img/2017-05-Sogilis-Christophe-Levet-Photographe-7461.jpg diff --git a/site/static/img/2017-05-ergogame-1024x576.jpeg b/blog/.vuepress/public/img/2017-05-ergogame-1024x576.jpeg similarity index 100% rename from site/static/img/2017-05-ergogame-1024x576.jpeg rename to blog/.vuepress/public/img/2017-05-ergogame-1024x576.jpeg diff --git a/site/static/img/2017-05-ergogame.jpeg b/blog/.vuepress/public/img/2017-05-ergogame.jpeg similarity index 100% rename from site/static/img/2017-05-ergogame.jpeg rename to blog/.vuepress/public/img/2017-05-ergogame.jpeg diff --git a/site/static/img/2017-05-tuleapcon2017.png b/blog/.vuepress/public/img/2017-05-tuleapcon2017.png similarity index 100% rename from site/static/img/2017-05-tuleapcon2017.png rename to blog/.vuepress/public/img/2017-05-tuleapcon2017.png diff --git a/site/static/img/2017-06-Article-IOT-1024x768.png b/blog/.vuepress/public/img/2017-06-Article-IOT-1024x768.png similarity index 100% rename from site/static/img/2017-06-Article-IOT-1024x768.png rename to blog/.vuepress/public/img/2017-06-Article-IOT-1024x768.png diff --git a/site/static/img/2017-06-Raspberry-300x159.jpg b/blog/.vuepress/public/img/2017-06-Raspberry-300x159.jpg similarity index 100% rename from site/static/img/2017-06-Raspberry-300x159.jpg rename to blog/.vuepress/public/img/2017-06-Raspberry-300x159.jpg diff --git a/site/static/img/2017-06-node_closed-300x200.jpg b/blog/.vuepress/public/img/2017-06-node_closed-300x200.jpg similarity index 100% rename from site/static/img/2017-06-node_closed-300x200.jpg rename to blog/.vuepress/public/img/2017-06-node_closed-300x200.jpg diff --git a/site/static/img/2017-06-node_closed.jpg b/blog/.vuepress/public/img/2017-06-node_closed.jpg similarity index 100% rename from site/static/img/2017-06-node_closed.jpg rename to blog/.vuepress/public/img/2017-06-node_closed.jpg diff --git a/site/static/img/2017-06-node_open-217x300.jpg b/blog/.vuepress/public/img/2017-06-node_open-217x300.jpg similarity index 100% rename from site/static/img/2017-06-node_open-217x300.jpg rename to blog/.vuepress/public/img/2017-06-node_open-217x300.jpg diff --git a/site/static/img/2017-06-test-utilisateur-basseresolutionjpg-1024x683.jpg b/blog/.vuepress/public/img/2017-06-test-utilisateur-basseresolutionjpg-1024x683.jpg similarity index 100% rename from site/static/img/2017-06-test-utilisateur-basseresolutionjpg-1024x683.jpg rename to blog/.vuepress/public/img/2017-06-test-utilisateur-basseresolutionjpg-1024x683.jpg diff --git a/site/static/img/2017-07-Be-Ready-1024x442.jpg b/blog/.vuepress/public/img/2017-07-Be-Ready-1024x442.jpg similarity index 100% rename from site/static/img/2017-07-Be-Ready-1024x442.jpg rename to blog/.vuepress/public/img/2017-07-Be-Ready-1024x442.jpg diff --git a/site/static/img/2017-07-Be-Ready-CasinoGame.jpg b/blog/.vuepress/public/img/2017-07-Be-Ready-CasinoGame.jpg similarity index 100% rename from site/static/img/2017-07-Be-Ready-CasinoGame.jpg rename to blog/.vuepress/public/img/2017-07-Be-Ready-CasinoGame.jpg diff --git a/site/static/img/2017-07-CasinoGame-1024x533.jpg b/blog/.vuepress/public/img/2017-07-CasinoGame-1024x533.jpg similarity index 100% rename from site/static/img/2017-07-CasinoGame-1024x533.jpg rename to blog/.vuepress/public/img/2017-07-CasinoGame-1024x533.jpg diff --git a/site/static/img/2017-07-CasinoGame.jpg b/blog/.vuepress/public/img/2017-07-CasinoGame.jpg similarity index 100% rename from site/static/img/2017-07-CasinoGame.jpg rename to blog/.vuepress/public/img/2017-07-CasinoGame.jpg diff --git a/site/static/img/2017-07-blog-class-diagramm.png b/blog/.vuepress/public/img/2017-07-blog-class-diagramm.png similarity index 100% rename from site/static/img/2017-07-blog-class-diagramm.png rename to blog/.vuepress/public/img/2017-07-blog-class-diagramm.png diff --git a/site/static/img/2017-07-blog-header-2-1024x309.png b/blog/.vuepress/public/img/2017-07-blog-header-2-1024x309.png similarity index 100% rename from site/static/img/2017-07-blog-header-2-1024x309.png rename to blog/.vuepress/public/img/2017-07-blog-header-2-1024x309.png diff --git a/site/static/img/2017-07-blog-solution-avec-duplications.png b/blog/.vuepress/public/img/2017-07-blog-solution-avec-duplications.png similarity index 100% rename from site/static/img/2017-07-blog-solution-avec-duplications.png rename to blog/.vuepress/public/img/2017-07-blog-solution-avec-duplications.png diff --git a/site/static/img/2017-08-IMG_5087-1200x800-1024x683.jpg b/blog/.vuepress/public/img/2017-08-IMG_5087-1200x800-1024x683.jpg similarity index 100% rename from site/static/img/2017-08-IMG_5087-1200x800-1024x683.jpg rename to blog/.vuepress/public/img/2017-08-IMG_5087-1200x800-1024x683.jpg diff --git a/site/static/img/2017-08-Margaux_1200x800-150x150.jpg b/blog/.vuepress/public/img/2017-08-Margaux_1200x800-150x150.jpg similarity index 100% rename from site/static/img/2017-08-Margaux_1200x800-150x150.jpg rename to blog/.vuepress/public/img/2017-08-Margaux_1200x800-150x150.jpg diff --git a/site/static/img/2017-08-licorne-sticker.png b/blog/.vuepress/public/img/2017-08-licorne-sticker.png similarity index 100% rename from site/static/img/2017-08-licorne-sticker.png rename to blog/.vuepress/public/img/2017-08-licorne-sticker.png diff --git a/site/static/img/2017-08-margaux-au-bureau.jpg b/blog/.vuepress/public/img/2017-08-margaux-au-bureau.jpg similarity index 100% rename from site/static/img/2017-08-margaux-au-bureau.jpg rename to blog/.vuepress/public/img/2017-08-margaux-au-bureau.jpg diff --git a/site/static/img/2017-10-Capture-2017-10-30-16.06.45.png b/blog/.vuepress/public/img/2017-10-Capture-2017-10-30-16.06.45.png similarity index 100% rename from site/static/img/2017-10-Capture-2017-10-30-16.06.45.png rename to blog/.vuepress/public/img/2017-10-Capture-2017-10-30-16.06.45.png diff --git a/site/static/img/2017-10-Capture-2017-10-30-16.20.18.png b/blog/.vuepress/public/img/2017-10-Capture-2017-10-30-16.20.18.png similarity index 100% rename from site/static/img/2017-10-Capture-2017-10-30-16.20.18.png rename to blog/.vuepress/public/img/2017-10-Capture-2017-10-30-16.20.18.png diff --git a/site/static/img/2017-10-Capture-2017-10-30-16.26.58.png b/blog/.vuepress/public/img/2017-10-Capture-2017-10-30-16.26.58.png similarity index 100% rename from site/static/img/2017-10-Capture-2017-10-30-16.26.58.png rename to blog/.vuepress/public/img/2017-10-Capture-2017-10-30-16.26.58.png diff --git a/site/static/img/2017-10-first_image.png b/blog/.vuepress/public/img/2017-10-first_image.png similarity index 100% rename from site/static/img/2017-10-first_image.png rename to blog/.vuepress/public/img/2017-10-first_image.png diff --git a/site/static/img/2017-10-text.md_.1.png b/blog/.vuepress/public/img/2017-10-text.md_.1.png similarity index 100% rename from site/static/img/2017-10-text.md_.1.png rename to blog/.vuepress/public/img/2017-10-text.md_.1.png diff --git a/site/static/img/2017-10-text.md_.10.png b/blog/.vuepress/public/img/2017-10-text.md_.10.png similarity index 100% rename from site/static/img/2017-10-text.md_.10.png rename to blog/.vuepress/public/img/2017-10-text.md_.10.png diff --git a/site/static/img/2017-10-text.md_.11.png b/blog/.vuepress/public/img/2017-10-text.md_.11.png similarity index 100% rename from site/static/img/2017-10-text.md_.11.png rename to blog/.vuepress/public/img/2017-10-text.md_.11.png diff --git a/site/static/img/2017-10-text.md_.12.png b/blog/.vuepress/public/img/2017-10-text.md_.12.png similarity index 100% rename from site/static/img/2017-10-text.md_.12.png rename to blog/.vuepress/public/img/2017-10-text.md_.12.png diff --git a/site/static/img/2017-10-text.md_.13.png b/blog/.vuepress/public/img/2017-10-text.md_.13.png similarity index 100% rename from site/static/img/2017-10-text.md_.13.png rename to blog/.vuepress/public/img/2017-10-text.md_.13.png diff --git a/site/static/img/2017-10-text.md_.14.png b/blog/.vuepress/public/img/2017-10-text.md_.14.png similarity index 100% rename from site/static/img/2017-10-text.md_.14.png rename to blog/.vuepress/public/img/2017-10-text.md_.14.png diff --git a/site/static/img/2017-10-text.md_.15.png b/blog/.vuepress/public/img/2017-10-text.md_.15.png similarity index 100% rename from site/static/img/2017-10-text.md_.15.png rename to blog/.vuepress/public/img/2017-10-text.md_.15.png diff --git a/site/static/img/2017-10-text.md_.16.png b/blog/.vuepress/public/img/2017-10-text.md_.16.png similarity index 100% rename from site/static/img/2017-10-text.md_.16.png rename to blog/.vuepress/public/img/2017-10-text.md_.16.png diff --git a/site/static/img/2017-10-text.md_.2.png b/blog/.vuepress/public/img/2017-10-text.md_.2.png similarity index 100% rename from site/static/img/2017-10-text.md_.2.png rename to blog/.vuepress/public/img/2017-10-text.md_.2.png diff --git a/site/static/img/2017-10-text.md_.3.png b/blog/.vuepress/public/img/2017-10-text.md_.3.png similarity index 100% rename from site/static/img/2017-10-text.md_.3.png rename to blog/.vuepress/public/img/2017-10-text.md_.3.png diff --git a/site/static/img/2017-10-text.md_.4.png b/blog/.vuepress/public/img/2017-10-text.md_.4.png similarity index 100% rename from site/static/img/2017-10-text.md_.4.png rename to blog/.vuepress/public/img/2017-10-text.md_.4.png diff --git a/site/static/img/2017-10-text.md_.5.png b/blog/.vuepress/public/img/2017-10-text.md_.5.png similarity index 100% rename from site/static/img/2017-10-text.md_.5.png rename to blog/.vuepress/public/img/2017-10-text.md_.5.png diff --git a/site/static/img/2017-10-text.md_.6.png b/blog/.vuepress/public/img/2017-10-text.md_.6.png similarity index 100% rename from site/static/img/2017-10-text.md_.6.png rename to blog/.vuepress/public/img/2017-10-text.md_.6.png diff --git a/site/static/img/2017-10-text.md_.7.png b/blog/.vuepress/public/img/2017-10-text.md_.7.png similarity index 100% rename from site/static/img/2017-10-text.md_.7.png rename to blog/.vuepress/public/img/2017-10-text.md_.7.png diff --git a/site/static/img/2017-10-text.md_.8.png b/blog/.vuepress/public/img/2017-10-text.md_.8.png similarity index 100% rename from site/static/img/2017-10-text.md_.8.png rename to blog/.vuepress/public/img/2017-10-text.md_.8.png diff --git a/site/static/img/2017-10-text.md_.9.png b/blog/.vuepress/public/img/2017-10-text.md_.9.png similarity index 100% rename from site/static/img/2017-10-text.md_.9.png rename to blog/.vuepress/public/img/2017-10-text.md_.9.png diff --git a/site/static/img/2017-11-20171031_144322-min-300x225.jpg b/blog/.vuepress/public/img/2017-11-20171031_144322-min-300x225.jpg similarity index 100% rename from site/static/img/2017-11-20171031_144322-min-300x225.jpg rename to blog/.vuepress/public/img/2017-11-20171031_144322-min-300x225.jpg diff --git a/site/static/img/2017-11-20171031_144455-min-300x225.jpg b/blog/.vuepress/public/img/2017-11-20171031_144455-min-300x225.jpg similarity index 100% rename from site/static/img/2017-11-20171031_144455-min-300x225.jpg rename to blog/.vuepress/public/img/2017-11-20171031_144455-min-300x225.jpg diff --git a/site/static/img/2017-11-cir-min-768x1024.jpg b/blog/.vuepress/public/img/2017-11-cir-min-768x1024.jpg similarity index 100% rename from site/static/img/2017-11-cir-min-768x1024.jpg rename to blog/.vuepress/public/img/2017-11-cir-min-768x1024.jpg diff --git a/site/static/img/2017-11-ofii-min.jpg b/blog/.vuepress/public/img/2017-11-ofii-min.jpg similarity index 100% rename from site/static/img/2017-11-ofii-min.jpg rename to blog/.vuepress/public/img/2017-11-ofii-min.jpg diff --git a/site/static/img/2017-11-preview-full-s_600ppi.png b/blog/.vuepress/public/img/2017-11-preview-full-s_600ppi.png similarity index 100% rename from site/static/img/2017-11-preview-full-s_600ppi.png rename to blog/.vuepress/public/img/2017-11-preview-full-s_600ppi.png diff --git a/site/static/img/2017-12-flipper.png b/blog/.vuepress/public/img/2017-12-flipper.png similarity index 100% rename from site/static/img/2017-12-flipper.png rename to blog/.vuepress/public/img/2017-12-flipper.png diff --git a/site/static/img/2018-01-WP_20171213_002-2-300x225.jpg b/blog/.vuepress/public/img/2018-01-WP_20171213_002-2-300x225.jpg similarity index 100% rename from site/static/img/2018-01-WP_20171213_002-2-300x225.jpg rename to blog/.vuepress/public/img/2018-01-WP_20171213_002-2-300x225.jpg diff --git a/site/static/img/2018-02-full_fsm.png b/blog/.vuepress/public/img/2018-02-full_fsm.png similarity index 100% rename from site/static/img/2018-02-full_fsm.png rename to blog/.vuepress/public/img/2018-02-full_fsm.png diff --git a/site/static/img/2018-06-483_256_Lucas-Felix.jpg b/blog/.vuepress/public/img/2018-06-483_256_Lucas-Felix.jpg similarity index 100% rename from site/static/img/2018-06-483_256_Lucas-Felix.jpg rename to blog/.vuepress/public/img/2018-06-483_256_Lucas-Felix.jpg diff --git a/site/static/img/2018-06-485_396_Antoine-Gervail.jpg b/blog/.vuepress/public/img/2018-06-485_396_Antoine-Gervail.jpg similarity index 100% rename from site/static/img/2018-06-485_396_Antoine-Gervail.jpg rename to blog/.vuepress/public/img/2018-06-485_396_Antoine-Gervail.jpg diff --git "a/site/static/img/2018-06-A-quelle-fr\303\251quence-facturez-vous-ces-diff\303\251rentes-prestations.png" "b/blog/.vuepress/public/img/2018-06-A-quelle-fr\303\251quence-facturez-vous-ces-diff\303\251rentes-prestations.png" similarity index 100% rename from "site/static/img/2018-06-A-quelle-fr\303\251quence-facturez-vous-ces-diff\303\251rentes-prestations.png" rename to "blog/.vuepress/public/img/2018-06-A-quelle-fr\303\251quence-facturez-vous-ces-diff\303\251rentes-prestations.png" diff --git "a/site/static/img/2018-06-Aupr\303\250s-des-clients-vous-vous-pr\303\251sentez-comment-1.png" "b/blog/.vuepress/public/img/2018-06-Aupr\303\250s-des-clients-vous-vous-pr\303\251sentez-comment-1.png" similarity index 100% rename from "site/static/img/2018-06-Aupr\303\250s-des-clients-vous-vous-pr\303\251sentez-comment-1.png" rename to "blog/.vuepress/public/img/2018-06-Aupr\303\250s-des-clients-vous-vous-pr\303\251sentez-comment-1.png" diff --git a/site/static/img/2018-06-Avec-qui-travaillez-vous-majoritairement.png b/blog/.vuepress/public/img/2018-06-Avec-qui-travaillez-vous-majoritairement.png similarity index 100% rename from site/static/img/2018-06-Avec-qui-travaillez-vous-majoritairement.png rename to blog/.vuepress/public/img/2018-06-Avec-qui-travaillez-vous-majoritairement.png diff --git a/site/static/img/2018-06-CNN.jpg b/blog/.vuepress/public/img/2018-06-CNN.jpg similarity index 100% rename from site/static/img/2018-06-CNN.jpg rename to blog/.vuepress/public/img/2018-06-CNN.jpg diff --git a/site/static/img/2018-06-Casino-game.png b/blog/.vuepress/public/img/2018-06-Casino-game.png similarity index 100% rename from site/static/img/2018-06-Casino-game.png rename to blog/.vuepress/public/img/2018-06-Casino-game.png diff --git a/site/static/img/2018-06-Comment-facturez-vous-vos-prestations-majoritairement..png b/blog/.vuepress/public/img/2018-06-Comment-facturez-vous-vos-prestations-majoritairement..png similarity index 100% rename from site/static/img/2018-06-Comment-facturez-vous-vos-prestations-majoritairement..png rename to blog/.vuepress/public/img/2018-06-Comment-facturez-vous-vos-prestations-majoritairement..png diff --git a/site/static/img/2018-06-Fulguro-premiere-iteration-300x300.png b/blog/.vuepress/public/img/2018-06-Fulguro-premiere-iteration-300x300.png similarity index 100% rename from site/static/img/2018-06-Fulguro-premiere-iteration-300x300.png rename to blog/.vuepress/public/img/2018-06-Fulguro-premiere-iteration-300x300.png diff --git a/site/static/img/2018-06-Kanban-300x226.png b/blog/.vuepress/public/img/2018-06-Kanban-300x226.png similarity index 100% rename from site/static/img/2018-06-Kanban-300x226.png rename to blog/.vuepress/public/img/2018-06-Kanban-300x226.png diff --git a/site/static/img/2018-06-Postit-action.png b/blog/.vuepress/public/img/2018-06-Postit-action.png similarity index 100% rename from site/static/img/2018-06-Postit-action.png rename to blog/.vuepress/public/img/2018-06-Postit-action.png diff --git a/site/static/img/2018-06-Recompense-cadenas-300x172.png b/blog/.vuepress/public/img/2018-06-Recompense-cadenas-300x172.png similarity index 100% rename from site/static/img/2018-06-Recompense-cadenas-300x172.png rename to blog/.vuepress/public/img/2018-06-Recompense-cadenas-300x172.png diff --git a/site/static/img/2018-06-Recompense-etoiles-300x168.png b/blog/.vuepress/public/img/2018-06-Recompense-etoiles-300x168.png similarity index 100% rename from site/static/img/2018-06-Recompense-etoiles-300x168.png rename to blog/.vuepress/public/img/2018-06-Recompense-etoiles-300x168.png diff --git a/site/static/img/2018-06-Recompense-pommes-300x227.png b/blog/.vuepress/public/img/2018-06-Recompense-pommes-300x227.png similarity index 100% rename from site/static/img/2018-06-Recompense-pommes-300x227.png rename to blog/.vuepress/public/img/2018-06-Recompense-pommes-300x227.png diff --git a/site/static/img/2018-06-Recompense-pommes.png b/blog/.vuepress/public/img/2018-06-Recompense-pommes.png similarity index 100% rename from site/static/img/2018-06-Recompense-pommes.png rename to blog/.vuepress/public/img/2018-06-Recompense-pommes.png diff --git a/site/static/img/2018-06-Tarifs.jpg b/blog/.vuepress/public/img/2018-06-Tarifs.jpg similarity index 100% rename from site/static/img/2018-06-Tarifs.jpg rename to blog/.vuepress/public/img/2018-06-Tarifs.jpg diff --git a/site/static/img/2018-06-Travaillez-vous-seuls.png b/blog/.vuepress/public/img/2018-06-Travaillez-vous-seuls.png similarity index 100% rename from site/static/img/2018-06-Travaillez-vous-seuls.png rename to blog/.vuepress/public/img/2018-06-Travaillez-vous-seuls.png diff --git a/site/static/img/2018-06-affiches_roles.jpg b/blog/.vuepress/public/img/2018-06-affiches_roles.jpg similarity index 100% rename from site/static/img/2018-06-affiches_roles.jpg rename to blog/.vuepress/public/img/2018-06-affiches_roles.jpg diff --git a/site/static/img/2018-06-avatars.jpg b/blog/.vuepress/public/img/2018-06-avatars.jpg similarity index 100% rename from site/static/img/2018-06-avatars.jpg rename to blog/.vuepress/public/img/2018-06-avatars.jpg diff --git "a/site/static/img/2018-06-bani\303\250re_questionnaire.png" "b/blog/.vuepress/public/img/2018-06-bani\303\250re_questionnaire.png" similarity index 100% rename from "site/static/img/2018-06-bani\303\250re_questionnaire.png" rename to "blog/.vuepress/public/img/2018-06-bani\303\250re_questionnaire.png" diff --git "a/site/static/img/2018-06-combien-dann\303\251es-dexp\303\251rience-avez-vous-3.png" "b/blog/.vuepress/public/img/2018-06-combien-dann\303\251es-dexp\303\251rience-avez-vous-3.png" similarity index 100% rename from "site/static/img/2018-06-combien-dann\303\251es-dexp\303\251rience-avez-vous-3.png" rename to "blog/.vuepress/public/img/2018-06-combien-dann\303\251es-dexp\303\251rience-avez-vous-3.png" diff --git "a/site/static/img/2018-06-fulguro-seconde-it\303\251ration-250x300.jpg" "b/blog/.vuepress/public/img/2018-06-fulguro-seconde-it\303\251ration-250x300.jpg" similarity index 100% rename from "site/static/img/2018-06-fulguro-seconde-it\303\251ration-250x300.jpg" rename to "blog/.vuepress/public/img/2018-06-fulguro-seconde-it\303\251ration-250x300.jpg" diff --git a/site/static/img/2018-06-illustration-prod.jpg b/blog/.vuepress/public/img/2018-06-illustration-prod.jpg similarity index 100% rename from site/static/img/2018-06-illustration-prod.jpg rename to blog/.vuepress/public/img/2018-06-illustration-prod.jpg diff --git a/site/static/img/2018-06-initiative-poker.png b/blog/.vuepress/public/img/2018-06-initiative-poker.png similarity index 100% rename from site/static/img/2018-06-initiative-poker.png rename to blog/.vuepress/public/img/2018-06-initiative-poker.png diff --git a/site/static/img/2018-06-initiative_poker.pdf b/blog/.vuepress/public/img/2018-06-initiative_poker.pdf similarity index 100% rename from site/static/img/2018-06-initiative_poker.pdf rename to blog/.vuepress/public/img/2018-06-initiative_poker.pdf diff --git "a/site/static/img/2018-06-j\303\251rome-doucet.jpg" "b/blog/.vuepress/public/img/2018-06-j\303\251rome-doucet.jpg" similarity index 100% rename from "site/static/img/2018-06-j\303\251rome-doucet.jpg" rename to "blog/.vuepress/public/img/2018-06-j\303\251rome-doucet.jpg" diff --git a/site/static/img/2018-06-quel-pourcentage-de-temps-facturez-vous-aux-clients.png b/blog/.vuepress/public/img/2018-06-quel-pourcentage-de-temps-facturez-vous-aux-clients.png similarity index 100% rename from site/static/img/2018-06-quel-pourcentage-de-temps-facturez-vous-aux-clients.png rename to blog/.vuepress/public/img/2018-06-quel-pourcentage-de-temps-facturez-vous-aux-clients.png diff --git a/site/static/img/2018-06-simon-denier.jpeg b/blog/.vuepress/public/img/2018-06-simon-denier.jpeg similarity index 100% rename from site/static/img/2018-06-simon-denier.jpeg rename to blog/.vuepress/public/img/2018-06-simon-denier.jpeg diff --git a/site/static/img/2018-07-Web2Day-featured.jpg b/blog/.vuepress/public/img/2018-07-Web2Day-featured.jpg similarity index 100% rename from site/static/img/2018-07-Web2Day-featured.jpg rename to blog/.vuepress/public/img/2018-07-Web2Day-featured.jpg diff --git a/site/static/img/2019-01-objectmother_builder_java.jpg b/blog/.vuepress/public/img/2019-01-objectmother_builder_java.jpg similarity index 100% rename from site/static/img/2019-01-objectmother_builder_java.jpg rename to blog/.vuepress/public/img/2019-01-objectmother_builder_java.jpg diff --git a/site/static/img/2019-12-data-test.jpeg b/blog/.vuepress/public/img/2019-12-data-test.jpeg similarity index 100% rename from site/static/img/2019-12-data-test.jpeg rename to blog/.vuepress/public/img/2019-12-data-test.jpeg diff --git a/site/static/img/2020-08-31-vuex-alternative/app_screenshot.jpeg b/blog/.vuepress/public/img/2020-08-31-vuex-alternative/app_screenshot.jpeg similarity index 100% rename from site/static/img/2020-08-31-vuex-alternative/app_screenshot.jpeg rename to blog/.vuepress/public/img/2020-08-31-vuex-alternative/app_screenshot.jpeg diff --git a/site/static/img/2020-08-31-vuex-alternative/title.jpeg b/blog/.vuepress/public/img/2020-08-31-vuex-alternative/title.jpeg similarity index 100% rename from site/static/img/2020-08-31-vuex-alternative/title.jpeg rename to blog/.vuepress/public/img/2020-08-31-vuex-alternative/title.jpeg diff --git a/site/static/img/2020-12-07-principes-caches-du-test.jpg b/blog/.vuepress/public/img/2020-12-07-principes-caches-du-test.jpg similarity index 100% rename from site/static/img/2020-12-07-principes-caches-du-test.jpg rename to blog/.vuepress/public/img/2020-12-07-principes-caches-du-test.jpg diff --git a/site/static/img/20201002/cycle_pulsar.jpg b/blog/.vuepress/public/img/20201002/cycle_pulsar.jpg similarity index 100% rename from site/static/img/20201002/cycle_pulsar.jpg rename to blog/.vuepress/public/img/20201002/cycle_pulsar.jpg diff --git a/site/static/img/20201002/cycle_v.jpg b/blog/.vuepress/public/img/20201002/cycle_v.jpg similarity index 100% rename from site/static/img/20201002/cycle_v.jpg rename to blog/.vuepress/public/img/20201002/cycle_v.jpg diff --git a/site/static/img/20201002/drone.jpg b/blog/.vuepress/public/img/20201002/drone.jpg similarity index 100% rename from site/static/img/20201002/drone.jpg rename to blog/.vuepress/public/img/20201002/drone.jpg diff --git a/site/static/img/20201002/exemple_xreq.jpg b/blog/.vuepress/public/img/20201002/exemple_xreq.jpg similarity index 100% rename from site/static/img/20201002/exemple_xreq.jpg rename to blog/.vuepress/public/img/20201002/exemple_xreq.jpg diff --git a/site/static/img/20201002/iteratif_incremental.jpg b/blog/.vuepress/public/img/20201002/iteratif_incremental.jpg similarity index 100% rename from site/static/img/20201002/iteratif_incremental.jpg rename to blog/.vuepress/public/img/20201002/iteratif_incremental.jpg diff --git a/site/static/img/20201002/xreq.jpg b/blog/.vuepress/public/img/20201002/xreq.jpg similarity index 100% rename from site/static/img/20201002/xreq.jpg rename to blog/.vuepress/public/img/20201002/xreq.jpg diff --git a/site/themes/sogilis/static/images/blog_preview_default.jpg b/blog/.vuepress/public/img/blog_preview_default.jpg similarity index 100% rename from site/themes/sogilis/static/images/blog_preview_default.jpg rename to blog/.vuepress/public/img/blog_preview_default.jpg diff --git a/site/themes/sogilis/static/images/favicon.png b/blog/.vuepress/public/img/favicon.png similarity index 100% rename from site/themes/sogilis/static/images/favicon.png rename to blog/.vuepress/public/img/favicon.png diff --git a/site/themes/sogilis/static/images/logo_sogilis.svg b/blog/.vuepress/public/img/logo_sogilis.svg similarity index 100% rename from site/themes/sogilis/static/images/logo_sogilis.svg rename to blog/.vuepress/public/img/logo_sogilis.svg diff --git a/site/themes/sogilis/static/images/logo_sogilis_alone.svg b/blog/.vuepress/public/img/logo_sogilis_alone.svg similarity index 100% rename from site/themes/sogilis/static/images/logo_sogilis_alone.svg rename to blog/.vuepress/public/img/logo_sogilis_alone.svg diff --git a/site/content/posts/2013-12-18-sogiday.md b/blog/_posts/2013-12-18-sogiday.md similarity index 97% rename from site/content/posts/2013-12-18-sogiday.md rename to blog/_posts/2013-12-18-sogiday.md index cf6dea455..8de010f6a 100644 --- a/site/content/posts/2013-12-18-sogiday.md +++ b/blog/_posts/2013-12-18-sogiday.md @@ -4,7 +4,7 @@ author: Luc date: 2013-12-18T08:08:00+00:00 image: /img/2013-12-Sogilis-Christophe-Levet-Photographe-7326.jpg categories: - - LA VIE À SOGILIS + - La vie à sogilis tag: - amélioration continue - sogiday @@ -56,7 +56,7 @@ Sur 11 personnes présentes, globalement, le temps a été bien investi : - 6 personnes ont voté 4 - 2 personnes ont voté 5 (temps très bien investi) -![](/img/2013-12-Sogiday-post-it.jpg "Post it du Sogiday") +![](/img/2013-12-Sogiday-post-it.jpg 'Post it du Sogiday') Nous avons en fin de journée décidé de renouveler l’expérience. diff --git a/site/content/posts/2014-01-29-histoire-4-premiers-mois-sogilis.md b/blog/_posts/2014-01-29-histoire-4-premiers-mois-sogilis.md similarity index 99% rename from site/content/posts/2014-01-29-histoire-4-premiers-mois-sogilis.md rename to blog/_posts/2014-01-29-histoire-4-premiers-mois-sogilis.md index f0261ffa3..68f702536 100644 --- a/site/content/posts/2014-01-29-histoire-4-premiers-mois-sogilis.md +++ b/blog/_posts/2014-01-29-histoire-4-premiers-mois-sogilis.md @@ -4,13 +4,13 @@ author: Luc date: 2014-01-29T05:59:00+00:00 image: /img/2014-01-Sogilis-Christophe-Levet-Photographe-7914.jpg categories: - - LA VIE À SOGILIS + - La vie à sogilis tags: - témoignage - vie interne --- -{{% disclaimer %}} + Après 4 mois passés à Sogilis, j'ai décidé de dresser un premier bilan : mon histoire. diff --git a/site/content/posts/2014-02-06-reading-xml-powershell-easy.md b/blog/_posts/2014-02-06-reading-xml-powershell-easy.md similarity index 93% rename from site/content/posts/2014-02-06-reading-xml-powershell-easy.md rename to blog/_posts/2014-02-06-reading-xml-powershell-easy.md index 3328f78bd..0c1932d9b 100644 --- a/site/content/posts/2014-02-06-reading-xml-powershell-easy.md +++ b/blog/_posts/2014-02-06-reading-xml-powershell-easy.md @@ -4,7 +4,7 @@ author: Luc date: 2014-02-06T08:07:00+00:00 image: /img/2016-04-2.La-vie-a-Sogilis.jpg categories: - - DÉVELOPPEMENT + - Développement logiciel tags: - code - dev @@ -15,7 +15,7 @@ It is amazing to see how simple we can read xml node values from powershell: **Here is a peace of xml file** -{{< highlight xml >}} +```xml @@ -34,11 +34,11 @@ It is amazing to see how simple we can read xml node values from powershell: -{{< /highlight >}} +``` **PowerShell converts XML elements to properties on .NET objects without the need to write any parsing code!** -{{< highlight powershell >}} +```powershell $coverageFileXml = “C:tempmyCoverageInfo.xml” [xml]$xml = (get-content $coverageFileXml) [int]$BlocksCovered = $xml.CoverageDSPriv.Module.BlocksCovered @@ -47,7 +47,7 @@ $coverageFileXml = “C:tempmyCoverageInfo.xml” $CoveragePercent = $BlocksCovered \* 100 / $TotalBlocks $CoveragePercent2Decimals = "{0:N2}" -f $CoveragePercent Write-Host "Coverage is $CoveragePercent2Decimals %" -{{< /highlight >}} +``` **Output is : Coverage is 48.62 %** diff --git a/site/content/posts/2014-02-19-git-base-donnee-entrailles.md b/blog/_posts/2014-02-19-git-base-donnee-entrailles.md similarity index 90% rename from site/content/posts/2014-02-19-git-base-donnee-entrailles.md rename to blog/_posts/2014-02-19-git-base-donnee-entrailles.md index 4f42fd1f0..78aa8e7e3 100644 --- a/site/content/posts/2014-02-19-git-base-donnee-entrailles.md +++ b/blog/_posts/2014-02-19-git-base-donnee-entrailles.md @@ -4,7 +4,7 @@ author: Yves date: 2014-02-19T14:58:00+00:00 image: /img/2016-04-Sogilis-Christophe-Levet-Photographe-7898.jpg categories: - - DÉVELOPPEMENT + - Développement logiciel tags: - code - développement @@ -16,7 +16,7 @@ Aujourd'hui, tout le monde ou presque utilise [Git](http://git-scm.org) comme ou Pour ma part c'est, entre autre, la lecture de [progit](http://git-scm.com/book) et du [chapitre dédié aux entrailles de Git](http://git-scm.com/book/en/Git-Internals) qui a commencé à m'y faire penser. En effet, si vous regardez (vraiment) bien, git peut être vu comme une “simple” base de donnée clé/valeur. Bon ok, simple mais avec tout de même une gestion de l'historique, faut pas oublier ce qui fait l'essence de Git quand même. Et c'est là tout l'intérêt de la chose. -Lors du dernier [Sogiday]({{< ref "posts/2013-12-18-sogiday.md" >}}), nous avons eu l'occasion de travailler sur ce point : utiliser Git comme moteur de stockage pour une application. +Lors du dernier [Sogiday](./2013-12-18-sogiday.md), nous avons eu l'occasion de travailler sur ce point : utiliser Git comme moteur de stockage pour une application. Voici donc la première partie relatant cette exploration : les entrailles deGit ! @@ -58,24 +58,24 @@ Git permet de créer des `blobs` de données, sans manipuler des fichiers. Et ç Allez, un petit exemple : -{{< highlight bash >}} +```bash $ echo '{"hello":"world!"}' | git hash-object -w --stdin 5be7403d30e5ecca8454ccd65391603a3adaf128 -{{< /highlight >}} +``` Et là, j'ai un `blob`. Alors oui, en vrai un fichier a été créé : -{{< highlight bash >}} +```bash $ find .git/objects -type f .git/objects/5b/e7403d30e5ecca8454ccd65391603a3adaf128 -{{< /highlight >}} +``` Et on peut évidemment demander à git de nous afficher la valeur correspondant à ce hash : -{{< highlight bash >}} +```bash $ git cat-file -p 5be7403d30e5ecca8454ccd65391603a3adaf128 {"hello":"world!"} -{{< /highlight >}} +``` Nous avons alors une base de donnée clé/valeur ! Pour une valeur on nous donne une clé, et si on demande à Git la valeur de la clé, il nous la donne. C'était pas si dur quand même 🙂 @@ -87,25 +87,25 @@ Le problème est que si nous changeons la valeur… la clé va changer. Et là n En effet, la solution se trouve derrière un arbre. -{{< highlight bash >}} +```bash $ git update-index --add --cacheinfo 100644 5be7403d30e5ecca8454ccd65391603a3adaf128 1.json $ git write-tree 3445f005406e920e5f91d2ff312c2a43794f97b0 -{{< /highlight >}} +``` Nous venons de créer un `tree` qui pointe vers notre `blob`. Mais surtout, nous venons d'ajouter une clé, `1.json`, qui elle ne devrait pas bouger à chaque modification de valeur. Et histoire que tout soit bien enregistré, nous pouvons commiter le `tree` ce qui va permettre de l'horodater, d'ajouter un message lors de la création. -{{< highlight bash >}} +```bash $ git commit-tree -m 'add 1' 3445f005406e920e5f91d2ff312c2a43794f97b0 3782de116baa41923d371e979034251d797a9d5a -{{< /highlight >}} +``` Histoire de vous assurer que tout s'est bien passé comme prévu, vous pouvez demander à Git de vous afficher les informations liées à ce hash : -{{< highlight bash >}} +```bash $ git show 3782de116baa41923d371e979034251d797a9d5a commit 3782de116baa41923d371e979034251d797a9d5a Author: Yves Brissaud <…@…> @@ -120,20 +120,20 @@ index 0000000..5be7403 +++ b/1.json @@ -0,0 +1 @@ +{"hello":"world!"} -{{< /highlight >}} +``` Il manque enfin une dernière chose : il nous faut une référence pour pouvoir y accéder facilement. -{{< highlight bash >}} +```bash $ git update-ref refs/heads/master 3782de116baa41923d371e979034251d797a9d5a -{{< /highlight >}} +``` Si on regarde le fichier `.git/refs/heads/master` nous avons bien le commit créé : -{{< highlight bash >}} +```bash $ cat .git/refs/heads/master 3782de116baa41923d371e979034251d797a9d5a -{{< /highlight >}} +``` Nous avons donc la structure suivante : @@ -141,10 +141,10 @@ Nous avons donc la structure suivante : Ok c'est bien joli tout ça, mais on va pas maintenant devoir faire un `checkout` et lire le fichier à la main quand même ? Evidemment non, sinon ça ne servirait à rien et le côté _base de données_ serait assez peu pratique. -{{< highlight bash >}} +```bash $ git show master:1.json {"hello":"world!"} -{{< /highlight >}} +``` ## Tree et blobs en chaine @@ -152,52 +152,52 @@ Alors, plutôt simple, non ? Maintenant, pour bien comprendre comment cela fonct Au lieu d'enregistrer mon blob sous `1.json` je vais le faire sous `items/json/1.json` : -{{< highlight bash >}} +```bash $ git update-index --add --cacheinfo 100644 5be7403d30e5ecca8454ccd65391603a3adaf128 items/json/1.json $ git write-tree 7eccf3dc9a845af20fca5f41c9d0f5077e167c12 -{{< /highlight >}} +``` A ce moment on ne voit pas beaucoup de différences. Et pourtant ! Si vous regardez dans le répertoire `.git/objects` vous n'avez pas 2 objets (le blob et le tree) mais 4 : -{{< highlight bash >}} +```bash $ find .git/objects -type f .git/objects/34/45f005406e920e5f91d2ff312c2a43794f97b0 .git/objects/5b/e7403d30e5ecca8454ccd65391603a3adaf128 .git/objects/7b/b488cbad32ad64e1a625920685f59322e9951f .git/objects/7e/ccf3dc9a845af20fca5f41c9d0f5077e167c12 -{{< /highlight >}} +``` Pour commencer à explorer, regardons ce qui est derrière `7eccf3dc9a845af20fca5f41c9d0f5077e167c12` : -{{< highlight bash >}} +```bash $ git show -p 7eccf3dc9a845af20fca5f41c9d0f5077e167c12 tree 7eccf3dc9a845af20fca5f41c9d0f5077e167c12 items/ -{{< /highlight >}} +``` Nous avons bien un `tree`, comme précédemment. Mais celui-ci contient uniquement `items/` comme enfant, et non `items/json/1.json` par exemple. Visualisons alors l'arbre : -{{< highlight bash >}} +```bash git ls-tree -r -t 7eccf3dc9a845af20fca5f41c9d0f5077e167c12 040000 tree 7bb488cbad32ad64e1a625920685f59322e9951f items 040000 tree 3445f005406e920e5f91d2ff312c2a43794f97b0 items/json 100644 blob 5be7403d30e5ecca8454ccd65391603a3adaf128 items/json/1.json -{{< /highlight >}} +``` Voici donc ce qui se cache derrière. Notre arbre contient un enfant `items`. Qui est un arbre contenant un enfant `json`. Qui est un arbre contenant un blob `1.json`. Vous pouvez aussi faire un `show` sur chaque hash pour voir le détail. Vous pouvez alors créer un commit et mettre à jour master : -{{< highlight bash >}} +```bash $ git commit-tree -m 'add 1' 7eccf3dc9a845af20fca5f41c9d0f5077e167c12 1838b5ba48b0f811d30ced3f249687e1780c544e $ git update-ref refs/heads/master 1838b5ba48b0f811d30ced3f249687e1780c544e -{{< /highlight >}} +``` La structure est donc désormais ainsi : @@ -205,16 +205,16 @@ La structure est donc désormais ainsi : Bien évidemment vous pouvez toujours récupérer le contenu par votre clé : -{{< highlight bash >}} +```bash $ git show master:items/json/1.json {"hello":"world!"} -{{< /highlight >}} +``` ## Conclusion Cette première partie à la découverte (succinte) de Git est terminée. Vous avez pu voir que git nous offre réellement la possibilité d'utiliser ses couches internes et nous expose son modèle de stockage. Ceci permet d'imaginer de nouvelles utilisations de git et surtout autrement qu'en wrappant les commandes Git de haut niveau ce qui serait ni agréable, ni fiable, ni amusant. -[Le prochain article]({{< ref "posts/2014-02-27-git-base-donnee-ruby.md" >}}) vous permettra de réaliser la même chose qu'ici mais intégré dans un code Ruby. +[Le prochain article](./2014-02-27-git-base-donnee-ruby.md) vous permettra de réaliser la même chose qu'ici mais intégré dans un code Ruby. Un dernier point sur git avant de se quitter. Sogilis dispense aussi des formations Git ! diff --git a/site/content/posts/2014-02-20-experimentation-llvm.md b/blog/_posts/2014-02-20-experimentation-llvm.md similarity index 93% rename from site/content/posts/2014-02-20-experimentation-llvm.md rename to blog/_posts/2014-02-20-experimentation-llvm.md index 2a7aa1e5a..a445285d5 100644 --- a/site/content/posts/2014-02-20-experimentation-llvm.md +++ b/blog/_posts/2014-02-20-experimentation-llvm.md @@ -4,7 +4,7 @@ author: Shanti date: 2014-02-20T10:01:00+00:00 image: /img/2016-04-2.Developpement-1.jpg categories: - - DÉVELOPPEMENT + - Développement logiciel tags: - llvm - sogiday @@ -33,7 +33,7 @@ La compilation a été un peu laborieuse en début de journée, surtout en consi Au final, le `CMakeLists.txt` ressemble à : -{{< highlight llvm >}} +```llvm set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules" "\${LLVM_ROOT}/share/llvm/cmake") @@ -54,7 +54,7 @@ link_directories( \${LLVM_LIBRARY_DIRS} ) add_executable(${PROJECT_NAME} ${SRC_LIST} ${LLVM_NATIVE_OBJECTS}) target_link_libraries(${PROJECT_NAME} \${REQ_LLVM_LIBRARIES}) -{{< /highlight >}} +``` Le fichier `handleLLVMOptions.cmake` dans `/usr/share/llvm/cmake` est utile pour les options préprocesseur. @@ -64,18 +64,18 @@ Finalement, une fois les détails réglés, CMake a su se faire oublier. Le fichier `test.sx` suivant a été utile tout au long de la journée pour tester le compilateur. Les instructions étaient ajoutées au fur et à mesure dans la fonction main : -{{< highlight llvm >}} +```llvm (def main (fun (puts "Hello World") (printfi "n = %d" (+ 1 2)))) -{{< /highlight >}} +``` Ce n’est pas grand chose, mais cela permet de démarrer un projet de compilateur. Pour information, le code [LLVM-IR](http://llvm.org/docs/LangRef.html) généré `test.ll` ressemble à : -{{< highlight llvm >}} +```llvm ; ModuleID = 'top' @0 = private unnamed_addr constant [15 x i8] c"22Hello World22A0" @@ -92,31 +92,31 @@ ret void declare void @puts(i8\*) declare void @printf(i8\*, ...) -{{< /highlight >}} +``` Ensuite, ce code est compilé en bytecode `test.bc` LLVM : -{{< highlight llvm >}} +```llvm llvm-as test.bc -{{< /highlight >}} +``` Puis en assembleur machine `test.S` : -{{< highlight llvm >}} +```llvm llc test.bc -o test.S -{{< /highlight >}} +``` Puis en exécutable : -{{< highlight llvm >}} +```llvm clang test.S -o test -{{< /highlight >}} +``` On peut aussi directement interpréter le bytecode avec une machine virtuelle : -{{< highlight llvm >}} +```llvm lli test.bc -{{< /highlight >}} +``` ## L’implémentation @@ -124,21 +124,21 @@ L’implémentation est relativement simple et tient en quelques classes. L’ar Ainsi, pour générer une des constantes, considérant `llvm::IRBuilder<> builder`, il faut exécuter : -{{< highlight llvm >}} +```llvm (llvm::Value*) builder.CreateGlobalStringPtr((std::string) string); (llvm::Value*) llvm::ConstantInt::get(llvm::Type::getInt32Ty(builder.getContext()), (int) integer); -{{< /highlight >}} +``` Pour ajouter une instruction d’addition au flot d’instructions `llvm::BasicBlock *entry`, il faut : -{{< highlight llvm >}} +```llvm (llvm::Value*) llvm::BinaryOperator::Create(llvm::Instruction::Add, (llvm::Value*) a, (llvm::Value\*) b, "", entry); -{{< /highlight >}} +``` Dans un module `llvm::Module *module`, pour déclarer des fonctions : -{{< highlight llvm >}} +```llvm // void puts(i8*); std::vector putsFuncTypeArgs(1); putsFuncTypeArgs[0] = builder.getInt8PtrTy(); @@ -154,11 +154,11 @@ llvm::FunctionType *printfFuncType = llvm::FunctionType::get(builder.getVoidTy() (printfFuncTypeArgs), true); // true if for vararg llvm::Function *printfFunc = llvm::Function::Create(printfFuncType, llvm::Function::ExternalLinkage, "printf", module); -{{< /highlight >}} +``` Et pour appeler ces fonctions : -{{< highlight llvm >}} +```llvm // puts std::vector putsFuncArgs(1); putsFuncArgs[0] = (Value*); @@ -169,11 +169,11 @@ std::vector printfFuncArgs(2); printfFuncArgs[0] = (Value*); printfFuncArgs[1] = (Value\*); llvm::CallInst::Create(printfFunc, llvm::makeArrayRef(printfFuncArgs), "", entry); -{{< /highlight >}} +``` Et finalement, le squelette de code pour créer un module : -{{< highlight llvm >}} +```llvm // Initialisation llvm::LLVMContext& context = llvm::getGlobalContext(); llvm::Module\* module = new llvm::Module("top", context); @@ -195,6 +195,6 @@ builder.SetInsertPoint(entry); llvm::PassManager pm; pm.add(llvm::createPrintModulePass(&llvm::outs())); pm.run(\*module); -{{< /highlight >}} +``` Le code est disponible sur GitHub dans le projet [minisexp](https://github.com/sogilis/minisexp). diff --git a/site/content/posts/2014-02-25-fosdem-2014.md b/blog/_posts/2014-02-25-fosdem-2014.md similarity index 99% rename from site/content/posts/2014-02-25-fosdem-2014.md rename to blog/_posts/2014-02-25-fosdem-2014.md index 33ba4f8fa..618a5c27b 100644 --- a/site/content/posts/2014-02-25-fosdem-2014.md +++ b/blog/_posts/2014-02-25-fosdem-2014.md @@ -4,7 +4,7 @@ author: Laurie date: 2014-02-25T14:04:00+00:00 image: /img/2014-02-Sogilis-Christophe-Levet-Photographe-7596.jpg categories: - - DÉVELOPPEMENT + - Développement logiciel tags: - firefox os - fosdem diff --git a/site/content/posts/2014-02-27-git-base-donnee-ruby.md b/blog/_posts/2014-02-27-git-base-donnee-ruby.md similarity index 91% rename from site/content/posts/2014-02-27-git-base-donnee-ruby.md rename to blog/_posts/2014-02-27-git-base-donnee-ruby.md index 671462961..a3b6e4f26 100644 --- a/site/content/posts/2014-02-27-git-base-donnee-ruby.md +++ b/blog/_posts/2014-02-27-git-base-donnee-ruby.md @@ -4,14 +4,14 @@ author: Yves date: 2014-02-27T12:26:00+00:00 image: /img/2015-03-Sogilis-Christophe-Levet-Photographe-7461.jpg categories: - - DÉVELOPPEMENT + - Développement logiciel tags: - git - ruby - sogiday --- -[La première partie consacrée]({{< ref "posts/2014-02-19-git-base-donnee-entrailles.md" >}}) à [Git](http://git-scm.org) vous a montré brièvement le stockage interne et surtout comment écrire une donnée, la lier à une clé et récupérer le contenu derrière cette clé. Pour cette deuxième partie, nous allons réaliser la même chose mais en utilisant un vrai langage de programmation, Ruby. +[La première partie consacrée](./2014-02-19-git-base-donnee-entrailles.md) à [Git](http://git-scm.org) vous a montré brièvement le stockage interne et surtout comment écrire une donnée, la lier à une clé et récupérer le contenu derrière cette clé. Pour cette deuxième partie, nous allons réaliser la même chose mais en utilisant un vrai langage de programmation, Ruby. ![YES](/img/2014-02-yes.gif) @@ -48,9 +48,9 @@ Voici donc la _traduction_ de toutes ces commandes en ruby afin de les inclures Pour l'installation, comme vous utilisez [gem](http://rubygems.org/) et [bundler](http://bundler.io/) (what else?) c'est super simple. Ajoutez -{{< highlight ruby >}} +```ruby gem 'rugged' -{{< /highlight >}} +``` dans votre `Gemfile` et installez le via `bundle install`. @@ -60,47 +60,49 @@ Tout d'abord, il vous faut accéder à votre répository git. Pour le créer : -{{< highlight ruby >}} +```ruby repo = Rugged::Repository.init_at('my_repo.git', :bare) -{{< /highlight >}} +``` ou pour y accéder s'il existe déjà : -{{< highlight ruby >}} +```ruby repo = Rugged::Repository.new('my_repo.git') -{{< /highlight >}} +``` > Oui, on utilise un dépôt _bare_, pas besoin d'avoir une copie de travail. La première vrai étape consiste donc à donner à git des données pour qu'il en crée un `blob`. Rien de plus simple : -{{< highlight ruby >}} +```ruby blob_oid = repo.write '{"hello":"world!"}' -{{< /highlight >}} +``` > Une solution sympa plutôt que de gérer le `hello world!` en chaîne de caractères est, par exemple, de passer par [yaml](http://www.yaml.org/) : > -> {{< highlight ruby >}} -require 'YAML' +> ```ruby +> require 'YAML' +> ``` hello = {"hello": "world!"} blob_oid = repo.write hello.to_yaml -{{< /highlight >}} + +```` > > L'avantage est que c'est dispo en standard en ruby, que la sortie est claire et lisible, qu'on peut facilement mapper des `struct` ruby et que c'est un format qui semble plutôt aisé à fusionner si besoin. La deuxième étape consiste à créer le `tree` correspondant. Pour ce faire nous allons avoir besoin d'un `index` qui va pouvoir faire le lien entre le blob et le chemin (la clé) que nous souhaitons. -{{< highlight ruby >}} +```ruby index = Rugged::Index.new index.add(:path => '1.json', :oid => blob_oid, :mode => 0100644) tree_oid = index.write_tree(repo) -{{< /highlight >}} +```` Enfin, il faut créer un commit et mettre à jour la référence (master) avec ce `tree`. Pour ce faire nous avons besoin de quelques informations supplémentaires par rapport à la version shell. En fait c'est pas que ce soit nouvelles informations, juste que git est allé cherché comme un grand ce dont il avait besoin dans sa configuration. -{{< highlight ruby >}} +```ruby options = {} options[:tree] = tree_oid # l'arbre que nous souhaitons committer options[:author] = {:email => '…@…', :name => 'sogilis', :time => Time.now} @@ -111,7 +113,7 @@ options[:parents] = repo.empty? ? [] : [repo.head.target] # on demande l'update de la ref ici, pas besoin d'une nouvelle commande options[:update_ref] = 'HEAD' Rugged::Commit.create(repo, options) -{{< /highlight >}} +``` Et voilà ! La même chose, en ruby ! @@ -121,7 +123,7 @@ Bon, ce que vous ne voyez pas ici c'est que la gestion de l'index est par contre Voici une version _naïve_ permettant de s'en affranchir, qui va créer un index en y ajoutant toutes les ressources voulues : -{{< highlight ruby >}} +```ruby index = Rugged::Index.new unless repo.empty? tree = repo.lookup(repo.head.target).tree @@ -130,7 +132,7 @@ unless repo.empty? :oid => entry[:oid], :mode => 0100644) end end -{{< /highlight >}} +``` En gros ce qui se passe : @@ -144,13 +146,13 @@ En gros ce qui se passe : Maintenant que vous avez compris le principe, on peut tout de même faire ça plus simplement : -{{< highlight ruby >}} +```ruby index = Rugged::Index.new unless repo.empty? tree = repo.lookup(repo.head.target).tree index.read_tree(tree) end -{{< /highlight >}} +``` `read_tree` s'occupe justement pour nous de lire tous les blobs correspondant à un arbre et les ajoute à l'index. @@ -162,7 +164,7 @@ Nous allons donc parcourir l'arbre relié à `head` à la recherche de la clé. Une première version est d'utiliser ce qu'on vient de faire avec l'index : -{{< highlight ruby >}} +```ruby def show repo, key return nil if repo.empty? tree = repo.lookup(repo.head.target).tree @@ -174,20 +176,20 @@ def show repo, key end nil end -{{< /highlight >}} +``` Ainsi `show(repo, '1.json')` retournera `'{"hello":"world!"}'` (si vous l'avez stocké en json). Et si vous avez fait du YAML, vous pourrez accéder à l'objet en faisant un `YAML.load(show(repo, '1.json'))`. Une autre solution est d'accéder directement à l'objet sous le `tree` : -{{< highlight ruby >}} +```ruby def show repo, key return nil if repo.empty? tree = repo.lookup(repo.head.target).tree oid = tree[key][:oid] repo.read(oid).data end -{{< /highlight >}} +``` Un peu plus simple, non ? Sauf que ce n'est pas suffisant. Cela marche très bien dans ce cas, mais la réalité est un poil plus complexe. Ceci ne fonctionne que si vous êtes dans le cas d'une clé posée à la racine. Si vous utilisez des clés “hiérarchiques” (comme des fichiers placés dans un répertoire) alors vous aurez une structure légèrement différente. @@ -199,19 +201,19 @@ L'arbre pointé par le commit va vous permettre d'accéder au répertoire. Il fa En plus clair, si votre clé est `items/1.json` cela ressemblera à : -{{< highlight ruby >}} +```ruby tree = repo.lookup(repo.head.target).tree items_oid = tree["items"][:oid] items_tree = repo.lookup(items_oid) oid = items_tree["1.json"][:oid] repo.read(oid).data -{{< /highlight >}} +``` Et évidemment il faudra faire ça de manière récursive si nécessaire. Voici donc une solution permettant d'accéder à vos données de manière un peu plus sympa : -{{< highlight ruby >}} +```ruby def show repo, key return nil if repo.empty? tree = repo.lookup(repo.head.target).tree @@ -228,7 +230,7 @@ def get_oid repo, tree, paths return nil if tree[key][:type] != :tree get_oid repo, repo.lookup(oid), paths end -{{< /highlight >}} +``` Evidemment votre `show(repo, '1.json')` fonctionnera toujours pareil. @@ -238,7 +240,7 @@ Il existe encore une autre solution pour récupérer les données liées à chem Une fois l'index relu via `index.read_tree(tree)` vous pouvez accéder à n'importe quel fichier facilement : -{{< highlight ruby >}} +```ruby def show repo, path return nil if repo.empty? tree = repo.lookup(repo.head.target).tree @@ -247,7 +249,7 @@ def show repo, path oid = index[path][:oid] repo.read(oid).data end -{{< /highlight >}} +``` A vous de choisir celle qui vous semble la plus intéressante 🙂 @@ -267,7 +269,7 @@ Par exemple, au lieu d'utiliser `repo.head.target` comme révision de base, il _ Par exemple, si on souhaite trouver le contenu de `1.json` à l'avant dernière révision, on peut le réaliser de la sorte : -{{< highlight ruby >}} +```ruby # arbre du commit de head tree = repo.lookup(repo.head.target).tree # récupération de la révision parente @@ -276,7 +278,7 @@ hash = tree.parents.first prev = repo.lookup(hash) # on récupère alors l'oid oid = prev['1.json'][:oid] -{{< /highlight >}} +``` On vient alors de naviguer dans l'historique afin de récupérer les données telles qu'elles étaient présentes dans le passé. @@ -286,10 +288,10 @@ Vous pouvez trouver le code de l'application de TodoList avec stockage dans git Pour le tester, rien de plus simple : -{{< highlight bash >}} +```bash bundle install bundle exec rackup -{{< /highlight >}} +``` et rendez-vous sur `http://localhost:9292`. Vous pouvez alors ajouter des items, les marquer comme fait et les supprimer. Et aussi naviguer dans les différentes versions existantes ! diff --git a/site/content/posts/2014-03-06-building-files-redo-simple-branch.md b/blog/_posts/2014-03-06-building-files-redo-simple-branch.md similarity index 95% rename from site/content/posts/2014-03-06-building-files-redo-simple-branch.md rename to blog/_posts/2014-03-06-building-files-redo-simple-branch.md index a4c88f5bf..c0b0585b5 100644 --- a/site/content/posts/2014-03-06-building-files-redo-simple-branch.md +++ b/blog/_posts/2014-03-06-building-files-redo-simple-branch.md @@ -1,10 +1,10 @@ --- -title: "Building files: redo, the simple branch" +title: 'Building files: redo, the simple branch' author: Shanti date: 2014-03-06T09:04:00+00:00 image: /img/2016-04-2.La-vie-a-Sogilis.jpg categories: - - DÉVELOPPEMENT + - Développement logiciel tags: - code - développement @@ -60,10 +60,10 @@ If that triggers your interest, you can look at the [project’s README](https:/ Let’s say you want to build `README.md` in `README.html`. Have a file called `default.html.do`: -{{< highlight bash >}} +```bash redo-ifchange "$2.md" Markdown.pl <"$2.md" >"\$3" -{{< /highlight >}} +``` Then, you simply run `redo README.html`. It will execute the script with three parameters: `README.html`, `README` and a temporary file. @@ -81,12 +81,12 @@ This solution is very elegant because the source is only parsed once. The file i Let’s see how a `default.o.do` would look like: -{{< highlight bash >}} +```bash redo-ifchange "$2.c" gcc -MD -MF "$2.d" -c -o "$3" "$2.c" read DEPS <"$2.d" redo-ifchange ${DEPS#\*:} -{{< /highlight >}} +``` - First, the source file itself is declared as a dependency. - Then, the file is built @@ -102,10 +102,10 @@ When building a target, you can use redo-stamp to record a hash instead of a tim Let’s look then at `config.h.do`: -{{< highlight bash >}} +```bash detect-config-h >"$3" redo-stamp <"$3" -{{< /highlight >}} +``` ## What’s the simple branch? @@ -117,7 +117,7 @@ The simple branch features a drastic change of implementation. It’s still pyth For each file recorded in redo, there is a corresponding file in the `.redo` directory with the same name, but ending with `.deps`. For example, you’d have for `src/main.c` a file `src/.redo/main.c.deps`. It would look like: -{{< highlight bash >}} +```bash redo.0 41 2848026 1386591363.1-1386591363.1-818-41-1796582 default.c.do @@ -125,7 +125,7 @@ redo.0 7bc09e7900651e7b8682eadc926105a868cf74cc . 1387535039.05-1387535039.03-96-41-2848029+1387535038 0 -{{< /highlight >}} +``` - `redo.0` is a constant tag containing the version of the format @@ -133,24 +133,24 @@ redo.0 - The list of dependencies: - {{< highlight bash >}} + ```bash 1386591363.1-1386591363.1-818-41-1796582 default.c.do 1386591363.1-1386591363.1-37-41-1796583 lib.h - {{< /highlight >}} + ``` The beginning of the line is the stamp for the dependency. On the right, there is the file name. - The stamp hash generated by `redo-stamp` (optional, line may be missing): - {{< highlight bash >}} + ```bash 7bc09e7900651e7b8682eadc926105a868cf74cc . - {{< /highlight >}} + ``` - The stamp based on file metadata of the last generated file: - {{< highlight bash >}} + ```bash 1387535039.05-1387535039.03-96-41-2848029+1387535038 - {{< /highlight >}} + ``` - The exit code of the last build of the file: `0` for success. @@ -176,18 +176,18 @@ This is only a recently implemented feature for the simple branch. It works by h Let’s say you have a program called `run-template` that generates a `testing.c` file and a `testing.h` file from `testing.tmpl`. You declare the master recipe (`testing.c.do`) this way: -{{< highlight bash >}} +```bash redo-ifchange "$2.tmpl" run-template --c-file="$3" --h-file="${3%.c}.h" <"$2.tmpl" -{{< /highlight >}} +``` What it does is depending on the `testing.tmpl` file and then telling `run-template` to put the master file (the `testing.c` file) in `$3` (the temporary file in the .redo directory). It also tells `run-template` to place the `testing.h` file in the same directory as the `testing.c` file. Placing a file here is enough as redo will scan this directory for extra targets. The slave recipe (`testing.h.do`) is just delegating build to `testing.c`: -{{< highlight bash >}} +```bash redo-delegate "testing.c" -{{< /highlight >}} +``` An outline of the files involved: diff --git a/site/content/posts/2014-03-11-management-sans-manager-sogilis.md b/blog/_posts/2014-03-11-management-sans-manager-sogilis.md similarity index 94% rename from site/content/posts/2014-03-11-management-sans-manager-sogilis.md rename to blog/_posts/2014-03-11-management-sans-manager-sogilis.md index 182f84530..e941b7f31 100644 --- a/site/content/posts/2014-03-11-management-sans-manager-sogilis.md +++ b/blog/_posts/2014-03-11-management-sans-manager-sogilis.md @@ -4,7 +4,7 @@ author: Tiphaine date: 2014-03-11T08:47:00+00:00 image: /img/2013-12-Sogilis-Christophe-Levet-Photographe-7326.jpg categories: - - NOTRE VISION + - Notre vision tags: - bonheur au travail - entreprise @@ -12,7 +12,7 @@ tags: - manager --- -{{% disclaimer %}} + Des salariés qui gèrent leur travail comme ils le veulent, des équipes sans supérieur hiérarchique. C’est le cas chez Sogilis. L’entreprise prône le management sans manager, et voici comment cela fonctionne. @@ -45,7 +45,7 @@ Le risque du management cellulaire pourrait être la micro-optimisation, c'est Différents évènements sont organisés dans le même objectif. Les salariés font une rétrospective générale chaque mois. Elle porte sur le fonctionnement actuel de l’entreprise, avec ses points positifs et ceux à améliorer. -Ils travaillent sur des projets, techniques ou non, durant les Sogidays (Lire l’article : [Les Sogidays]({{< ref "posts/2013-12-18-sogiday.md" >}}) ). Les projets choisis concernent l'ensemble de la société. +Ils travaillent sur des projets, techniques ou non, durant les Sogidays (Lire l’article : [Les Sogidays](./2013-12-18-sogiday.md) ). Les projets choisis concernent l'ensemble de la société. Des Coding Dojos sont également organisés pour permettre le partage des connaissances entre les cellules. diff --git a/site/content/posts/2014-03-25-sogilis-soutient-grenoble-frenchtech.md b/blog/_posts/2014-03-25-sogilis-soutient-grenoble-frenchtech.md similarity index 98% rename from site/content/posts/2014-03-25-sogilis-soutient-grenoble-frenchtech.md rename to blog/_posts/2014-03-25-sogilis-soutient-grenoble-frenchtech.md index cc6e6ad13..5c1abf336 100644 --- a/site/content/posts/2014-03-25-sogilis-soutient-grenoble-frenchtech.md +++ b/blog/_posts/2014-03-25-sogilis-soutient-grenoble-frenchtech.md @@ -4,7 +4,7 @@ author: Tiphaine date: 2014-03-25T09:18:00+00:00 image: /img/2016-04-1.GRENOBLE.jpg categories: - - LA VIE À SOGILIS + - La vie à sogilis tags: - DigiGre - Digital Grenoble diff --git a/site/content/posts/2014-04-01-mouvement-nosql.md b/blog/_posts/2014-04-01-mouvement-nosql.md similarity index 98% rename from site/content/posts/2014-04-01-mouvement-nosql.md rename to blog/_posts/2014-04-01-mouvement-nosql.md index f93f97876..a7900a38b 100644 --- a/site/content/posts/2014-04-01-mouvement-nosql.md +++ b/blog/_posts/2014-04-01-mouvement-nosql.md @@ -4,7 +4,7 @@ author: Alexandre Dumont date: 2014-04-01T08:28:00+00:00 image: /img/2015-03-Sogilis-Christophe-Levet-Photographe-7461.jpg categories: - - DÉVELOPPEMENT + - Développement logiciel tags: - columndb - documentdb @@ -15,7 +15,7 @@ tags: Ces dernières années témoignent d’un engouement certain autour des technologies permettant l’accumulation, l’analyse et la transformation de données très volumineuses (réseaux sociaux notamment). Dans l’optique de supporter des volumes de données grandissants, il est nécessaire de délocaliser les procédures de traitement sur différentes machines et de mutualiser les ressources de façon transparente pour l’utilisateur final. -![](/img/2014-04-tumblr_inline_n3b2avvoZ21sc5im4.png "NoSql") +![](/img/2014-04-tumblr_inline_n3b2avvoZ21sc5im4.png 'NoSql') ## Le NoSQL, c’est quoi ? diff --git a/site/content/posts/2014-04-01-re-apprentissage.md b/blog/_posts/2014-04-01-re-apprentissage.md similarity index 99% rename from site/content/posts/2014-04-01-re-apprentissage.md rename to blog/_posts/2014-04-01-re-apprentissage.md index 6fee0d90b..26e704db6 100644 --- a/site/content/posts/2014-04-01-re-apprentissage.md +++ b/blog/_posts/2014-04-01-re-apprentissage.md @@ -4,7 +4,7 @@ author: Valentin date: 2014-04-01T07:26:00+00:00 image: /img/2016-04-0.Haut_page.jpg categories: - - DÉVELOPPEMENT + - Développement logiciel tags: - apprentissage --- diff --git a/site/content/posts/2014-04-10-retrospective-mensuelle.md b/blog/_posts/2014-04-10-retrospective-mensuelle.md similarity index 95% rename from site/content/posts/2014-04-10-retrospective-mensuelle.md rename to blog/_posts/2014-04-10-retrospective-mensuelle.md index 0771b9374..f5f47dbd9 100644 --- a/site/content/posts/2014-04-10-retrospective-mensuelle.md +++ b/blog/_posts/2014-04-10-retrospective-mensuelle.md @@ -4,13 +4,13 @@ author: Tiphaine date: 2014-04-10T08:54:00+00:00 image: /img/2016-05-IMG_0527.png categories: - - NOTRE VISION + - Notre vision tags: - amélioration continue - vie interne --- -{{% disclaimer %}} + Comme nous l’avons évoqué précédemment sur le blog, nous organisons une rétrospective générale chaque mois chez Sogilis dans un but d’amélioration continue. Elle permet de prendre du recul sur le fonctionnement passé de l’entreprise en interne et de faire évoluer l'équipe dans le même sens. @@ -22,7 +22,7 @@ Elle est menée par un facilitateur, différent à chaque rétro, qui structure Un des buts de la rétrospective est de **trouver des solutions aux problèmes soulevés** et de **prendre action pour résoudre ces problèmes**. Toutes les actions (définies par des tâches concrètes) sont notées sur Basecamp et assignées à une personne qui doit effectuer l’action pour la rétro suivante. -Par exemple, lors de la rétrospective de septembre le **manque de partage entre les cellules** avait été soulevé. Les [Sogidays]({{< ref "posts/2013-12-18-sogiday.md" >}}) ont été mis en place pour améliorer ce point. +Par exemple, lors de la rétrospective de septembre le **manque de partage entre les cellules** avait été soulevé. Les [Sogidays](./2013-12-18-sogiday.md) ont été mis en place pour améliorer ce point. Les règles ont étés rappelées par le facilitateur lors de la dernière rétro. À gauche, les choses à faire en rétro (“oui”), à droite, les choses à ne pas faire (“non”). diff --git a/site/content/posts/2014-04-15-retour-vitrine-dexcellence-numerique-iserois.md b/blog/_posts/2014-04-15-retour-vitrine-dexcellence-numerique-iserois.md similarity index 74% rename from site/content/posts/2014-04-15-retour-vitrine-dexcellence-numerique-iserois.md rename to blog/_posts/2014-04-15-retour-vitrine-dexcellence-numerique-iserois.md index 7839b53fd..4b7ee7a70 100644 --- a/site/content/posts/2014-04-15-retour-vitrine-dexcellence-numerique-iserois.md +++ b/blog/_posts/2014-04-15-retour-vitrine-dexcellence-numerique-iserois.md @@ -4,7 +4,7 @@ author: Tiphaine date: 2014-04-15T10:03:00+00:00 image: /img/2016-04-gre.png categories: - - LA VIE À SOGILIS + - La vie à sogilis tags: - conférence - événement @@ -16,4 +16,6 @@ La Vitrine d’Excellence du Numérique Isérois se tenait le 25 mars dernier au Christophe Baillon, le fondateur et dirigeant de Sogilis, y a présenté l’entreprise. Il a notamment mis en avant sa culture et son type de management, qui enthousiasme les clients et épanouit les salariés. -{{< youtube QVf5MVV4XZQ >}} + + + diff --git a/site/content/posts/2014-04-29-qt-applications-desktop-mobiles-1.md b/blog/_posts/2014-04-29-qt-applications-desktop-mobiles-1.md similarity index 79% rename from site/content/posts/2014-04-29-qt-applications-desktop-mobiles-1.md rename to blog/_posts/2014-04-29-qt-applications-desktop-mobiles-1.md index 117d99605..f4bbc4ff5 100644 --- a/site/content/posts/2014-04-29-qt-applications-desktop-mobiles-1.md +++ b/blog/_posts/2014-04-29-qt-applications-desktop-mobiles-1.md @@ -4,7 +4,7 @@ author: Yves date: 2014-04-29T08:47:00+00:00 image: /img/2015-03-Sogilis-Christophe-Levet-Photographe-7503.jpg categories: - - DÉVELOPPEMENT + - Développement logiciel tags: - c++ - mobile @@ -38,33 +38,32 @@ Allez non, en fait il reste un dernier point intéressant : QtMobile. Il est auj Et c'est donc tout cela que nous allons voir dans cette série d'articles : -- créer une application avec un cœur applicatif en `C++ -` +- créer une application avec un cœur applicatif en `C++ ` - avoir une interface en QML et voir la liaison entre ces deux parties (initialement l'objectif portait surtout sur ce point mais ça a un peu dérivé ;-)) - l'exécuter partout ! ## Sommaire -- [L'application de base]({{< relref "posts/2014-04-29-qt-applications-desktop-mobiles-1.md#base-app" >}}) - - [Qt et application Qt Quick]({{< relref "posts/2014-04-29-qt-applications-desktop-mobiles-1.md#qt" >}}) - - [Prérequis]({{< relref "posts/2014-04-29-qt-applications-desktop-mobiles-1.md#req" >}}) - - [Créer un projet Qt Quick]({{< relref "posts/2014-04-29-qt-applications-desktop-mobiles-1.md#quick" >}}) - - [Découverte rapide]({{< relref "posts/2014-04-29-qt-applications-desktop-mobiles-1.md#discover" >}}) - - [2048]({{< relref "posts/2014-04-30-qt-applications-desktop-mobiles-2.md#2048" >}}) - - [2048.c]({{< relref "posts/2014-04-30-qt-applications-desktop-mobiles-2.md#c" >}}) - - [2048 en Qt]({{< relref "posts/2014-04-30-qt-applications-desktop-mobiles-2.md#qt" >}}) -- [Interface QML]({{< relref "posts/2014-05-06-qt-applications-desktop-mobiles-3.md#interface" >}}) - - [Board]({{< relref "posts/2014-05-06-qt-applications-desktop-mobiles-3.md#board" >}}) - - [Affichage du plateau]({{< relref "posts/2014-05-06-qt-applications-desktop-mobiles-3.md#display" >}}) - - [Un peu de style]({{< relref "posts/2014-05-06-qt-applications-desktop-mobiles-3.md#style" >}}) - - [Déplacement et jeu]({{< ref "posts/2014-05-07-qt-applications-desktop-mobiles-4.md" >}}) - - [Score et status]({{< ref "posts/2014-05-13-qt-applications-desktop-mobiles-5.md" >}}) - - [Responsive design]({{< ref "posts/2014-05-14-qt-applications-desktop-mobiles-6.md" >}}) -- [Et pour les mobiles]({{< relref "posts/2014-05-15-qt-applications-desktop-mobiles-7.md#mobile" >}}) - - [Gestures]({{< relref "posts/2014-05-15-qt-applications-desktop-mobiles-7.md#gestures" >}}) - - [iOS]({{< relref "posts/2014-05-15-qt-applications-desktop-mobiles-7.md#ios" >}}) - - [Android]({{< relref "posts/2014-05-15-qt-applications-desktop-mobiles-7.md#android" >}}) -- [Fin ?]({{< relref "posts/2014-05-15-qt-applications-desktop-mobiles-7.md#end" >}}) +- [L'application de base](./2014-04-29-qt-applications-desktop-mobiles-1.md#base-app) + - [Qt et application Qt Quick](./2014-04-29-qt-applications-desktop-mobiles-1.md#qt) + - [Prérequis](./2014-04-29-qt-applications-desktop-mobiles-1.md#req) + - [Créer un projet Qt Quick](./2014-04-29-qt-applications-desktop-mobiles-1.md#quick) + - [Découverte rapide](./2014-04-29-qt-applications-desktop-mobiles-1.md#discover) + - [2048](./2014-04-30-qt-applications-desktop-mobiles-2.md#2048) + - [2048.c](./2014-04-30-qt-applications-desktop-mobiles-2.md#c) + - [2048 en Qt](./2014-04-30-qt-applications-desktop-mobiles-2.md#qt) +- [Interface QML](./2014-05-06-qt-applications-desktop-mobiles-3.md#interface) + - [Board](./2014-05-06-qt-applications-desktop-mobiles-3.md#board) + - [Affichage du plateau](./2014-05-06-qt-applications-desktop-mobiles-3.md#display) + - [Un peu de style](./2014-05-06-qt-applications-desktop-mobiles-3.md#style) + - [Déplacement et jeu](./2014-05-07-qt-applications-desktop-mobiles-4.md) + - [Score et status](./2014-05-13-qt-applications-desktop-mobiles-5.md) + - [Responsive design](./2014-05-14-qt-applications-desktop-mobiles-6.md) +- [Et pour les mobiles](./2014-05-15-qt-applications-desktop-mobiles-7.md#mobile) + - [Gestures](./2014-05-15-qt-applications-desktop-mobiles-7.md#gestures) + - [iOS](./2014-05-15-qt-applications-desktop-mobiles-7.md#ios) + - [Android](./2014-05-15-qt-applications-desktop-mobiles-7.md#android) +- [Fin ?](./2014-05-15-qt-applications-desktop-mobiles-7.md#end) ## L'application de base @@ -124,7 +123,7 @@ Voici donc les fichiers que vous devriez avoir : Le fichier `main.cpp` doit ressembler à cela : -{{< highlight cpp >}} +```cpp #include #include @@ -137,7 +136,7 @@ int main(int argc, char \*argv[]) return app.exec(); } -{{< /highlight >}} +``` En gros, il instancie une application Qml et charge un fichier qml représentant notre interface. @@ -147,7 +146,7 @@ import QtQuick 2.2 import QtQuick.Window 2.1 -{{< highlight cpp >}} +```cpp Window { visible: true width: 360 @@ -165,7 +164,7 @@ Window { anchors.centerIn: parent } } -{{< /highlight >}} +``` Comme vous pouvez le voir c'est plutôt très lisible et compréhensible : un rectangle est dessiné et contient le texte _“Hello World”_. Une zone de capture des évènements de la souris est placée et qui lorsqu'on clique dedans ferme la fenêtre. diff --git a/site/content/posts/2014-04-30-qt-applications-desktop-mobiles-2.md b/blog/_posts/2014-04-30-qt-applications-desktop-mobiles-2.md similarity index 59% rename from site/content/posts/2014-04-30-qt-applications-desktop-mobiles-2.md rename to blog/_posts/2014-04-30-qt-applications-desktop-mobiles-2.md index f8731c52c..0271032c5 100644 --- a/site/content/posts/2014-04-30-qt-applications-desktop-mobiles-2.md +++ b/blog/_posts/2014-04-30-qt-applications-desktop-mobiles-2.md @@ -4,7 +4,7 @@ author: Yves date: 2014-04-30T08:12:00+00:00 image: /img/2014-04-Sogilis-Christophe-Levet-Photographe-7433.jpg categories: - - DÉVELOPPEMENT + - Développement logiciel tags: - c++ - mobile @@ -16,26 +16,26 @@ Suite de la découverte de la programmation desktop et mobile avec Qt. ## Sommaire -- [L'application de base]({{< relref "posts/2014-04-29-qt-applications-desktop-mobiles-1.md#base-app" >}}) - - [Qt et application Qt Quick]({{< relref "posts/2014-04-29-qt-applications-desktop-mobiles-1.md#qt" >}}) - - [Prérequis]({{< relref "posts/2014-04-29-qt-applications-desktop-mobiles-1.md#req" >}}) - - [Créer un projet Qt Quick]({{< relref "posts/2014-04-29-qt-applications-desktop-mobiles-1.md#quick" >}}) - - [Découverte rapide]({{< relref "posts/2014-04-29-qt-applications-desktop-mobiles-1.md#discover" >}}) - - [2048]({{< relref "posts/2014-04-30-qt-applications-desktop-mobiles-2.md#2048" >}}) - - [2048.c]({{< relref "posts/2014-04-30-qt-applications-desktop-mobiles-2.md#c" >}}) - - [2048 en Qt]({{< relref "posts/2014-04-30-qt-applications-desktop-mobiles-2.md#qt" >}}) -- [Interface QML]({{< relref "posts/2014-05-06-qt-applications-desktop-mobiles-3.md#interface" >}}) - - [Board]({{< relref "posts/2014-05-06-qt-applications-desktop-mobiles-3.md#board" >}}) - - [Affichage du plateau]({{< relref "posts/2014-05-06-qt-applications-desktop-mobiles-3.md#display" >}}) - - [Un peu de style]({{< relref "posts/2014-05-06-qt-applications-desktop-mobiles-3.md#style" >}}) - - [Déplacement et jeu]({{< ref "posts/2014-05-07-qt-applications-desktop-mobiles-4.md" >}}) - - [Score et status]({{< ref "posts/2014-05-13-qt-applications-desktop-mobiles-5.md" >}}) - - [Responsive design]({{< ref "posts/2014-05-14-qt-applications-desktop-mobiles-6.md" >}}) -- [Et pour les mobiles]({{< relref "posts/2014-05-15-qt-applications-desktop-mobiles-7.md#mobile" >}}) - - [Gestures]({{< relref "posts/2014-05-15-qt-applications-desktop-mobiles-7.md#gestures" >}}) - - [iOS]({{< relref "posts/2014-05-15-qt-applications-desktop-mobiles-7.md#ios" >}}) - - [Android]({{< relref "posts/2014-05-15-qt-applications-desktop-mobiles-7.md#android" >}}) -- [Fin ?]({{< relref "posts/2014-05-15-qt-applications-desktop-mobiles-7.md#end" >}}) +- [L'application de base](./2014-04-29-qt-applications-desktop-mobiles-1.md#base-app) + - [Qt et application Qt Quick](./2014-04-29-qt-applications-desktop-mobiles-1.md#qt) + - [Prérequis](./2014-04-29-qt-applications-desktop-mobiles-1.md#req) + - [Créer un projet Qt Quick](./2014-04-29-qt-applications-desktop-mobiles-1.md#quick) + - [Découverte rapide](./2014-04-29-qt-applications-desktop-mobiles-1.md#discover) + - [2048](./2014-04-30-qt-applications-desktop-mobiles-2.md#2048) + - [2048.c](./2014-04-30-qt-applications-desktop-mobiles-2.md#c) + - [2048 en Qt](./2014-04-30-qt-applications-desktop-mobiles-2.md#qt) +- [Interface QML](./2014-05-06-qt-applications-desktop-mobiles-3.md#interface) + - [Board](./2014-05-06-qt-applications-desktop-mobiles-3.md#board) + - [Affichage du plateau](./2014-05-06-qt-applications-desktop-mobiles-3.md#display) + - [Un peu de style](./2014-05-06-qt-applications-desktop-mobiles-3.md#style) + - [Déplacement et jeu](./2014-05-07-qt-applications-desktop-mobiles-4.md) + - [Score et status](./2014-05-13-qt-applications-desktop-mobiles-5.md) + - [Responsive design](./2014-05-14-qt-applications-desktop-mobiles-6.md) +- [Et pour les mobiles](./2014-05-15-qt-applications-desktop-mobiles-7.md#mobile) + - [Gestures](./2014-05-15-qt-applications-desktop-mobiles-7.md#gestures) + - [iOS](./2014-05-15-qt-applications-desktop-mobiles-7.md#ios) + - [Android](./2014-05-15-qt-applications-desktop-mobiles-7.md#android) +- [Fin ?](./2014-05-15-qt-applications-desktop-mobiles-7.md#end) ### 2048 @@ -47,7 +47,7 @@ Le but étant d'explorer des technologies, pas de développer un jeu, je me suis C'est une implémentation en console pour Linux. Bon ça fonctionne aussi sous mac 😉 -Le code est relativement simple, il y a une matrice 4*4 qui représente le plateau. Les mouvements sont simplifiés puisque seul le mouvement vers le haut est implémenté. Les autres mouvements sont une combinaison de rotation vers la droite et de déplacement vers le haut (une rotation, un déplacement, 3 rotations donne un déplacement vers la gauche par exemple). +Le code est relativement simple, il y a une matrice 4\*4 qui représente le plateau. Les mouvements sont simplifiés puisque seul le mouvement vers le haut est implémenté. Les autres mouvements sont une combinaison de rotation vers la droite et de déplacement vers le haut (une rotation, un déplacement, 3 rotations donne un déplacement vers la gauche par exemple). J'ai gardé les principes de base, juste quelques petites modifications mineurs entre autre au niveau de ces déplacements. @@ -57,7 +57,7 @@ J'ai donc ajouté une classe `Board` qui hérite de `QObject`. Il est bienvenue Voici le _header_ de cette classe. -{{< highlight cpp >}} +```cpp #ifndef BOARD_H #define BOARD_H @@ -121,7 +121,7 @@ private: }; #endif // BOARD_H -{{< /highlight >}} +``` Au niveau des méthodes publiques rien de très complexe : @@ -136,7 +136,7 @@ Je ne détaille pas vraiment les méthodes privées ni l'implémentation, ce n'e Juste histoire de voir que tout fonctionne bien j'ai changé le `main` comme suit : -{{< highlight cpp >}} +```cpp int main(int argc, char *argv[]) { Q_UNUSED(argc) @@ -155,7 +155,7 @@ int main(int argc, char *argv[]) return 0; } -{{< /highlight >}} +``` C'est pas super beau mais ça permet de se rendre compte que oui ça fonctionne. @@ -167,7 +167,6 @@ Petit apparté rapide. Normalement vous devriez avoir un warning du genre : C'est normal, j'utilise `auto` qui vient de `C++11`. Il faut donc l'activer dans le fichier `qt2048.pro` : -{{< highlight cpp >}} +```cpp CONFIG+=c++11 -{{< /highlight >}} - +``` diff --git a/site/content/posts/2014-05-06-qt-applications-desktop-mobiles-3.md b/blog/_posts/2014-05-06-qt-applications-desktop-mobiles-3.md similarity index 80% rename from site/content/posts/2014-05-06-qt-applications-desktop-mobiles-3.md rename to blog/_posts/2014-05-06-qt-applications-desktop-mobiles-3.md index f6dc280fe..379b7cf7b 100644 --- a/site/content/posts/2014-05-06-qt-applications-desktop-mobiles-3.md +++ b/blog/_posts/2014-05-06-qt-applications-desktop-mobiles-3.md @@ -4,7 +4,7 @@ author: Yves date: 2014-05-06T08:05:00+00:00 image: /img/2016-04-2.Formations-e1461591900149.jpg categories: - - DÉVELOPPEMENT + - Développement logiciel tags: - c++ - mobile @@ -16,26 +16,26 @@ Suite de la découverte de la programmation desktop et mobile avec Qt. ## Sommaire -- [L'application de base]({{< relref "posts/2014-04-29-qt-applications-desktop-mobiles-1.md#base-app" >}}) - - [Qt et application Qt Quick]({{< relref "posts/2014-04-29-qt-applications-desktop-mobiles-1.md#qt" >}}) - - [Prérequis]({{< relref "posts/2014-04-29-qt-applications-desktop-mobiles-1.md#req" >}}) - - [Créer un projet Qt Quick]({{< relref "posts/2014-04-29-qt-applications-desktop-mobiles-1.md#quick" >}}) - - [Découverte rapide]({{< relref "posts/2014-04-29-qt-applications-desktop-mobiles-1.md#discover" >}}) - - [2048]({{< relref "posts/2014-04-30-qt-applications-desktop-mobiles-2.md#2048" >}}) - - [2048.c]({{< relref "posts/2014-04-30-qt-applications-desktop-mobiles-2.md#c" >}}) - - [2048 en Qt]({{< relref "posts/2014-04-30-qt-applications-desktop-mobiles-2.md#qt" >}}) -- [Interface QML]({{< relref "posts/2014-05-06-qt-applications-desktop-mobiles-3.md#interface" >}}) - - [Board]({{< relref "posts/2014-05-06-qt-applications-desktop-mobiles-3.md#board" >}}) - - [Affichage du plateau]({{< relref "posts/2014-05-06-qt-applications-desktop-mobiles-3.md#display" >}}) - - [Un peu de style]({{< relref "posts/2014-05-06-qt-applications-desktop-mobiles-3.md#style" >}}) - - [Déplacement et jeu]({{< ref "posts/2014-05-07-qt-applications-desktop-mobiles-4.md" >}}) - - [Score et status]({{< ref "posts/2014-05-13-qt-applications-desktop-mobiles-5.md" >}}) - - [Responsive design]({{< ref "posts/2014-05-14-qt-applications-desktop-mobiles-6.md" >}}) -- [Et pour les mobiles]({{< relref "posts/2014-05-15-qt-applications-desktop-mobiles-7.md#mobile" >}}) - - [Gestures]({{< relref "posts/2014-05-15-qt-applications-desktop-mobiles-7.md#gestures" >}}) - - [iOS]({{< relref "posts/2014-05-15-qt-applications-desktop-mobiles-7.md#ios" >}}) - - [Android]({{< relref "posts/2014-05-15-qt-applications-desktop-mobiles-7.md#android" >}}) -- [Fin ?]({{< relref "posts/2014-05-15-qt-applications-desktop-mobiles-7.md#end" >}}) +- [L'application de base](./2014-04-29-qt-applications-desktop-mobiles-1.md#base-app) + - [Qt et application Qt Quick](./2014-04-29-qt-applications-desktop-mobiles-1.md#qt) + - [Prérequis](./2014-04-29-qt-applications-desktop-mobiles-1.md#req) + - [Créer un projet Qt Quick](./2014-04-29-qt-applications-desktop-mobiles-1.md#quick) + - [Découverte rapide](./2014-04-29-qt-applications-desktop-mobiles-1.md#discover) + - [2048](./2014-04-30-qt-applications-desktop-mobiles-2.md#2048) + - [2048.c](./2014-04-30-qt-applications-desktop-mobiles-2.md#c) + - [2048 en Qt](./2014-04-30-qt-applications-desktop-mobiles-2.md#qt) +- [Interface QML](./2014-05-06-qt-applications-desktop-mobiles-3.md#interface) + - [Board](./2014-05-06-qt-applications-desktop-mobiles-3.md#board) + - [Affichage du plateau](./2014-05-06-qt-applications-desktop-mobiles-3.md#display) + - [Un peu de style](./2014-05-06-qt-applications-desktop-mobiles-3.md#style) + - [Déplacement et jeu](./2014-05-07-qt-applications-desktop-mobiles-4.md) + - [Score et status](./2014-05-13-qt-applications-desktop-mobiles-5.md) + - [Responsive design](./2014-05-14-qt-applications-desktop-mobiles-6.md) +- [Et pour les mobiles](./2014-05-15-qt-applications-desktop-mobiles-7.md#mobile) + - [Gestures](./2014-05-15-qt-applications-desktop-mobiles-7.md#gestures) + - [iOS](./2014-05-15-qt-applications-desktop-mobiles-7.md#ios) + - [Android](./2014-05-15-qt-applications-desktop-mobiles-7.md#android) +- [Fin ?](./2014-05-15-qt-applications-desktop-mobiles-7.md#end) ## Interface QML @@ -49,7 +49,7 @@ Comme indiqué, l'affichage va se faire en utilisant les possibilités de QML. Pour commencer en douceur, on peut modifier le fichier `qml` pour afficher une grille de 4 cases de côté. Et pour ça il convient d'utiliser un objet… `Grid` -{{< highlight cpp >}} +```cpp import QtQuick 2.2 import QtQuick.Window 2.1 @@ -78,7 +78,7 @@ Window { } } } -{{< /highlight >}} +``` Ceci devrait vous donner une grille de 4 cases de côté, avec dans chacune son indice. @@ -98,29 +98,29 @@ Pour que cela fonctionne il faut rajouter deux choses, un compteur d'éléments Mais avant cela, il faut juste déclarer notre plateau de jeu. Pour ce faire c'est simple, rajoutez un membre privé de type `Board`. -{{< highlight cpp >}} +```cpp private: Board board_; -{{< /highlight >}} +``` Maintenant, implémentez l'accès au compteur d'éléments. Dans le `.h` : -{{< highlight cpp >}} +```cpp public: int rowCount(const QModelIndex &parent = QModelIndex()) const; -{{< /highlight >}} +``` Dans le `.cpp` : -{{< highlight cpp >}} +```cpp int BoardModel::rowCount(const QModelIndex &parent) const { Q_UNUSED(parent) return kSize * kSize; } -{{< /highlight >}} +``` `kSize` est défini dans `board.h`. N'oubliez pas qu'on a linéarisé le plateau 😉 @@ -128,16 +128,16 @@ La deuxième méthode à implémenter est l'accès à une donnée en fonction de Dans le `.h` -{{< highlight cpp >}} +```cpp public: QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; -{{< /highlight >}} +``` Vous noterez qu'il est question de _role_. Il s'agit d'un mécanisme permettant de demander différents types de valeurs pour une même donnée. Par exemple demander la valeur, la couleur et le libellé d'une donnée. Pour cet exemple je ne vais pas l'utiliser et demande donc simplement la valeur à afficher. Voici l'implémentation : -{{< highlight cpp >}} +```cpp QVariant BoardModel::data(const QModelIndex &index, int role) const { t_index x, y; if (index.row() < 0 || index.row() >= kSize * kSize) { @@ -151,7 +151,7 @@ QVariant BoardModel::data(const QModelIndex &index, int role) const { } return QVariant(); } -{{< /highlight >}} +``` Le fonctionnement est très simple, si l'index est hors des bornes ou que le rôle demandé n'est pas l'affichage je retourne un `QVariant` vide. Sinon je récupère les deux coordonnées à partir de l'indice et récupère la donnée depuis le plateau. @@ -159,16 +159,16 @@ Ceci permet d'obtenir l'ensemble des valeurs à afficher. Il ne reste plus qu'à Rajouter dans le `main.cpp` (par exemple avant le `load`) : -{{< highlight cpp >}} +```cpp BoardModel board; engine.rootContext()->setContextProperty("board", &board); -{{< /highlight >}} +``` > Il faudra évidemment inclure `boardmodel.h` mais aussi `` dans le `main.cpp`. De cette façon nous pouvons accéder à notre objet via la variable `board` dans le QML. `board` devient donc le `model` de notre `Repeater` et la valeur à afficher devient `display` : -{{< highlight cpp >}} +```cpp import QtQuick 2.2 import QtQuick.Window 2.1 @@ -197,7 +197,7 @@ Window { } } } -{{< /highlight >}} +``` Et voilà, votre plateau de jeu est affiché. Vous avez les 16 cases avec normalement deux cases qui ont 2 ou 4 dedans et les autres 0. @@ -224,7 +224,7 @@ On va donc justement extraire l'affichage d'une tuile et s'occuper des points `1 Et comme nous allons réaliser cela dans un autre fichier, un autre composant, il va falloir faire un lien entre la valeur issue du modèle du `Repeater` (`display`) et une propriété du composant (que j'ai nommé `value`). Voici donc le fichier QML `Tile.qml` -{{< highlight cpp >}} +```cpp import QtQuick 2.2 Rectangle { @@ -305,7 +305,7 @@ Rectangle { color: "black" } } -{{< /highlight >}} +``` Vous noterez aussi deux petites choses : @@ -316,14 +316,14 @@ Pour visualiser le rendu, vous pouvez passer par le menu `Outils/Externe/Qt Quic Maintenant que nous avons notre tuile, il faut l'afficher dans le plateau. Rien de plus simple, remplacez juste l'élément `delegate` par votre tuile tout en affectant la propriété `value` : -{{< highlight cpp >}} +```cpp Repeater { model: board delegate: Tile { value: display } } -{{< /highlight >}} +``` Pour ce qui est du point `4.`, je vous laisse définir la couleur de fond à `#baaa9e`. diff --git a/site/content/posts/2014-05-07-qt-applications-desktop-mobiles-4.md b/blog/_posts/2014-05-07-qt-applications-desktop-mobiles-4.md similarity index 69% rename from site/content/posts/2014-05-07-qt-applications-desktop-mobiles-4.md rename to blog/_posts/2014-05-07-qt-applications-desktop-mobiles-4.md index 51a010254..8cb14380b 100644 --- a/site/content/posts/2014-05-07-qt-applications-desktop-mobiles-4.md +++ b/blog/_posts/2014-05-07-qt-applications-desktop-mobiles-4.md @@ -4,7 +4,7 @@ author: Yves date: 2014-05-07T08:26:00+00:00 image: /img/2014-08-Sogilis-Christophe-Levet-Photographe-7517.jpg categories: - - DÉVELOPPEMENT + - Développement logiciel tags: - c++ - mobile @@ -16,26 +16,26 @@ Suite de la découverte de la programmation desktop et mobile avec Qt. ## Sommaire -- [L'application de base]({{< relref "posts/2014-04-29-qt-applications-desktop-mobiles-1.md#base-app" >}}) - - [Qt et application Qt Quick]({{< relref "posts/2014-04-29-qt-applications-desktop-mobiles-1.md#qt" >}}) - - [Prérequis]({{< relref "posts/2014-04-29-qt-applications-desktop-mobiles-1.md#req" >}}) - - [Créer un projet Qt Quick]({{< relref "posts/2014-04-29-qt-applications-desktop-mobiles-1.md#quick" >}}) - - [Découverte rapide]({{< relref "posts/2014-04-29-qt-applications-desktop-mobiles-1.md#discover" >}}) - - [2048]({{< relref "posts/2014-04-30-qt-applications-desktop-mobiles-2.md#2048" >}}) - - [2048.c]({{< relref "posts/2014-04-30-qt-applications-desktop-mobiles-2.md#c" >}}) - - [2048 en Qt]({{< relref "posts/2014-04-30-qt-applications-desktop-mobiles-2.md#qt" >}}) -- [Interface QML]({{< relref "posts/2014-05-06-qt-applications-desktop-mobiles-3.md#interface" >}}) - - [Board]({{< relref "posts/2014-05-06-qt-applications-desktop-mobiles-3.md#board" >}}) - - [Affichage du plateau]({{< relref "posts/2014-05-06-qt-applications-desktop-mobiles-3.md#display" >}}) - - [Un peu de style]({{< relref "posts/2014-05-06-qt-applications-desktop-mobiles-3.md#style" >}}) - - [Déplacement et jeu]({{< ref "posts/2014-05-07-qt-applications-desktop-mobiles-4.md" >}}) - - [Score et status]({{< ref "posts/2014-05-13-qt-applications-desktop-mobiles-5.md" >}}) - - [Responsive design]({{< ref "posts/2014-05-14-qt-applications-desktop-mobiles-6.md" >}}) -- [Et pour les mobiles]({{< relref "posts/2014-05-15-qt-applications-desktop-mobiles-7.md#mobile" >}}) - - [Gestures]({{< relref "posts/2014-05-15-qt-applications-desktop-mobiles-7.md#gestures" >}}) - - [iOS]({{< relref "posts/2014-05-15-qt-applications-desktop-mobiles-7.md#ios" >}}) - - [Android]({{< relref "posts/2014-05-15-qt-applications-desktop-mobiles-7.md#android" >}}) -- [Fin ?]({{< relref "posts/2014-05-15-qt-applications-desktop-mobiles-7.md#end" >}}) +- [L'application de base](./2014-04-29-qt-applications-desktop-mobiles-1.md#base-app) + - [Qt et application Qt Quick](./2014-04-29-qt-applications-desktop-mobiles-1.md#qt) + - [Prérequis](./2014-04-29-qt-applications-desktop-mobiles-1.md#req) + - [Créer un projet Qt Quick](./2014-04-29-qt-applications-desktop-mobiles-1.md#quick) + - [Découverte rapide](./2014-04-29-qt-applications-desktop-mobiles-1.md#discover) + - [2048](./2014-04-30-qt-applications-desktop-mobiles-2.md#2048) + - [2048.c](./2014-04-30-qt-applications-desktop-mobiles-2.md#c) + - [2048 en Qt](./2014-04-30-qt-applications-desktop-mobiles-2.md#qt) +- [Interface QML](./2014-05-06-qt-applications-desktop-mobiles-3.md#interface) + - [Board](./2014-05-06-qt-applications-desktop-mobiles-3.md#board) + - [Affichage du plateau](./2014-05-06-qt-applications-desktop-mobiles-3.md#display) + - [Un peu de style](./2014-05-06-qt-applications-desktop-mobiles-3.md#style) + - [Déplacement et jeu](./2014-05-07-qt-applications-desktop-mobiles-4.md) + - [Score et status](./2014-05-13-qt-applications-desktop-mobiles-5.md) + - [Responsive design](./2014-05-14-qt-applications-desktop-mobiles-6.md) +- [Et pour les mobiles](./2014-05-15-qt-applications-desktop-mobiles-7.md#mobile) + - [Gestures](./2014-05-15-qt-applications-desktop-mobiles-7.md#gestures) + - [iOS](./2014-05-15-qt-applications-desktop-mobiles-7.md#ios) + - [Android](./2014-05-15-qt-applications-desktop-mobiles-7.md#android) +- [Fin ?](./2014-05-15-qt-applications-desktop-mobiles-7.md#end) #### Déplacement et jeu @@ -45,7 +45,7 @@ Le fonctionnement est très simple. Il faut écouter le clavier et sur les touch Il faut définir un _handler_ sur l'évènement d'appuis de touche et tester la touche : -{{< highlight cpp >}} +```cpp Window { //... Rectangle { @@ -65,7 +65,7 @@ Window { } } } -{{< /highlight >}} +``` La propriété `focus` permet de donner le focus à l'item et donc d'écouter les entrées. Et comme il n'est pas possible de le faire au niveau de l'objet `Window`un `Rectangle` a été introduit en lui spécifiant de remplir tout le parent via `anchors.fill: parent`. @@ -73,7 +73,7 @@ Jusque là c'était la partie la plus évidente. Par contre, comment appeler les Nous allons utiliser l'objet `BoardModel` qui existe déjà (oui c'est pas forcément la meilleure architecture mais pour comprendre les concepts c'est pas mal) et lui ajouter les méthodes correspondantes. Mais ça ne suffit pas pour les appeler depuis le QML. Pour ce faire il faut les rendre invocables, via la macro `Q_INVOKABLE` : -{{< highlight cpp >}} +```cpp // BoardModel.h public: Q_INVOKABLE void moveUp(); @@ -97,7 +97,7 @@ void BoardModel::moveDown() { void BoardModel::moveLeft() { board_.moveLeft(); } -{{< /highlight >}} +``` Dans ce cas, vous pouvez remplir le `switch` avec les appels, par exemple `board.moveLeft();` @@ -107,12 +107,12 @@ Et oui, car le principe de QML est entre autre de reposer sur l'excellent systè Bon, la première implémentation naïve est d'émettre le signal `dataChanged` depuis les méthodes `move*` de `BoardModel`. Par exemple pour `moveUp` : -{{< highlight cpp >}} +```cpp void BoardModel::moveUp() { board_.moveUp(); emit dataChanged(createIndex(0, 0), createIndex(rowCount() - 1, 0)); } -{{< /highlight >}} +``` Et voilà, cela fonctionne ! Votre 2048 est utilisable ! @@ -122,7 +122,7 @@ Par contre je sais pas vous, mais je trouve que c'est pas si génial. L'un des  On va donc créer un signal dans `Board` et l'émettre lorsque le déplacement a été effectué : -{{< highlight cpp >}} +```cpp // board.h signals: void boardChangedAfterMovement(); @@ -136,11 +136,11 @@ bool Board::move(Directions direction) { } //... } -{{< /highlight >}} +``` Au niveau de `BoardModel` on va créer un slot `onDataChanged` qui va émettre le signal `dataChanged`. Et on va connecter le signal `boardChangedAfterMovement`de `Board` au slot créé. -{{< highlight cpp >}} +```cpp // boardmodel.h public slots: void onDataChanged(); @@ -154,7 +154,7 @@ BoardModel::BoardModel(QObject *parent) : void BoardModel::onDataChanged() { emit dataChanged(createIndex(0, 0), createIndex(rowCount() - 1, 0)); } -{{< /highlight >}} +``` Evidemment, les méthodes `move*` reviennent comme précédemment avec juste l'appel à `board_`. diff --git a/site/content/posts/2014-05-13-qt-applications-desktop-mobiles-5.md b/blog/_posts/2014-05-13-qt-applications-desktop-mobiles-5.md similarity index 65% rename from site/content/posts/2014-05-13-qt-applications-desktop-mobiles-5.md rename to blog/_posts/2014-05-13-qt-applications-desktop-mobiles-5.md index 332d7029f..1e97bd600 100644 --- a/site/content/posts/2014-05-13-qt-applications-desktop-mobiles-5.md +++ b/blog/_posts/2014-05-13-qt-applications-desktop-mobiles-5.md @@ -4,7 +4,7 @@ author: Yves date: 2014-05-13T07:20:00+00:00 image: /img/2016-05-Sogilis-produits.png categories: - - DÉVELOPPEMENT + - Développement logiciel tags: - c++ - mobile @@ -16,26 +16,26 @@ Suite de la découverte de la programmation desktop et mobile avec Qt. ## Sommaire -- [L'application de base]({{< relref "posts/2014-04-29-qt-applications-desktop-mobiles-1.md#base-app" >}}) - - [Qt et application Qt Quick]({{< relref "posts/2014-04-29-qt-applications-desktop-mobiles-1.md#qt" >}}) - - [Prérequis]({{< relref "posts/2014-04-29-qt-applications-desktop-mobiles-1.md#req" >}}) - - [Créer un projet Qt Quick]({{< relref "posts/2014-04-29-qt-applications-desktop-mobiles-1.md#quick" >}}) - - [Découverte rapide]({{< relref "posts/2014-04-29-qt-applications-desktop-mobiles-1.md#discover" >}}) - - [2048]({{< relref "posts/2014-04-30-qt-applications-desktop-mobiles-2.md#2048" >}}) - - [2048.c]({{< relref "posts/2014-04-30-qt-applications-desktop-mobiles-2.md#c" >}}) - - [2048 en Qt]({{< relref "posts/2014-04-30-qt-applications-desktop-mobiles-2.md#qt" >}}) -- [Interface QML]({{< relref "posts/2014-05-06-qt-applications-desktop-mobiles-3.md#interface" >}}) - - [Board]({{< relref "posts/2014-05-06-qt-applications-desktop-mobiles-3.md#board" >}}) - - [Affichage du plateau]({{< relref "posts/2014-05-06-qt-applications-desktop-mobiles-3.md#display" >}}) - - [Un peu de style]({{< relref "posts/2014-05-06-qt-applications-desktop-mobiles-3.md#style" >}}) - - [Déplacement et jeu]({{< ref "posts/2014-05-07-qt-applications-desktop-mobiles-4.md" >}}) - - [Score et status]({{< ref "posts/2014-05-13-qt-applications-desktop-mobiles-5.md" >}}) - - [Responsive design]({{< ref "posts/2014-05-14-qt-applications-desktop-mobiles-6.md" >}}) -- [Et pour les mobiles]({{< relref "posts/2014-05-15-qt-applications-desktop-mobiles-7.md#mobile" >}}) - - [Gestures]({{< relref "posts/2014-05-15-qt-applications-desktop-mobiles-7.md#gestures" >}}) - - [iOS]({{< relref "posts/2014-05-15-qt-applications-desktop-mobiles-7.md#ios" >}}) - - [Android]({{< relref "posts/2014-05-15-qt-applications-desktop-mobiles-7.md#android" >}}) -- [Fin ?]({{< relref "posts/2014-05-15-qt-applications-desktop-mobiles-7.md#end" >}}) +- [L'application de base](./2014-04-29-qt-applications-desktop-mobiles-1.md#base-app) + - [Qt et application Qt Quick](./2014-04-29-qt-applications-desktop-mobiles-1.md#qt) + - [Prérequis](./2014-04-29-qt-applications-desktop-mobiles-1.md#req) + - [Créer un projet Qt Quick](./2014-04-29-qt-applications-desktop-mobiles-1.md#quick) + - [Découverte rapide](./2014-04-29-qt-applications-desktop-mobiles-1.md#discover) + - [2048](./2014-04-30-qt-applications-desktop-mobiles-2.md#2048) + - [2048.c](./2014-04-30-qt-applications-desktop-mobiles-2.md#c) + - [2048 en Qt](./2014-04-30-qt-applications-desktop-mobiles-2.md#qt) +- [Interface QML](./2014-05-06-qt-applications-desktop-mobiles-3.md#interface) + - [Board](./2014-05-06-qt-applications-desktop-mobiles-3.md#board) + - [Affichage du plateau](./2014-05-06-qt-applications-desktop-mobiles-3.md#display) + - [Un peu de style](./2014-05-06-qt-applications-desktop-mobiles-3.md#style) + - [Déplacement et jeu](./2014-05-07-qt-applications-desktop-mobiles-4.md) + - [Score et status](./2014-05-13-qt-applications-desktop-mobiles-5.md) + - [Responsive design](./2014-05-14-qt-applications-desktop-mobiles-6.md) +- [Et pour les mobiles](./2014-05-15-qt-applications-desktop-mobiles-7.md#mobile) + - [Gestures](./2014-05-15-qt-applications-desktop-mobiles-7.md#gestures) + - [iOS](./2014-05-15-qt-applications-desktop-mobiles-7.md#ios) + - [Android](./2014-05-15-qt-applications-desktop-mobiles-7.md#android) +- [Fin ?](./2014-05-15-qt-applications-desktop-mobiles-7.md#end) ### Score et status @@ -45,13 +45,13 @@ On va commencer par afficher deux composants textes qui vont contenir le résul Pour ça, commencez par ajouter `QtQuick.Layouts` à votre `qml` : -{{< highlight cpp >}} +```cpp import QtQtuick.Layouts 1.1 -{{< /highlight >}} +``` Puis les layouts et les champs textes : -{{< highlight cpp >}} +```cpp Rectangle { //... ColumnLayout { @@ -78,7 +78,7 @@ Rectangle { //... } } -{{< /highlight >}} +``` Bon comme vous le voyez, rien de très spécial ici ça reste simple et logique. @@ -86,7 +86,7 @@ Côté `C++` il faut pouvoir récupérer la valeur du score et celle des status. La première idée est de créer une méthode (je le fais pour le score, vous le ferez pour le reste 😉 ) `score` qui va interroger `board_`. -{{< highlight cpp >}} +```cpp // boardmodel.h public: int score() const; @@ -95,7 +95,7 @@ public: int BoardModel::score() const { return board_.score(); } -{{< /highlight >}} +``` Si vous vous souvenez bien de l'étape précédente, les méthodes de déplacement avaient été préfixées de `Q_INVOKABLE`. Ici pas besoin, nous allons faire autrement et transformer le tout en une _propriété_. Une propriété va être accessible par le QML comme une variable (et non une méthode), et nous allons définir 4 choses : @@ -108,7 +108,7 @@ Dans notre cas il n'y a pas de méthode d'écriture mais vous noterez que cela  On va donc appeler la variable `score`, son type est `int`, la méthode à appeler pour lire la valeur est `score` et comme signal on va faire original, `scoreChanged`. -{{< highlight cpp >}} +```cpp // boardmodel.h class BoardModel : public QAbstractListModel { @@ -122,13 +122,13 @@ signals: //... } -{{< /highlight >}} +``` Et voilà, vous pouvez maintenant remplacer `"score"` dans votre fichier `qml`par `board.score`. Trop facile, non ? Par contre, vous pouvez constater que la valeur ne change pas… en effet, comme avant, il faut émettre le signal lorsque la valeur change pour demander son rafraichissement. De la même manière, un signal va être émit par la classe `Board`lorsque le score change, et nous allons juste propager ce signal vers le signal `scoreChanged` que nous venons de créer. -{{< highlight cpp >}} +```cpp // board.h signals: void scoreChanged(); @@ -148,7 +148,7 @@ BoardModel::BoardModel(QObject *parent) : connect(&board_, &Board::boardChangedAfterMovement, this, &BoardModel::onDataChanged); connect(&board_, &Board::scoreChanged, this, &BoardModel::scoreChanged); } -{{< /highlight >}} +``` Et voilà, rien de plus à faire, ça fonctionne déjà ! Agréable, non ? diff --git a/site/content/posts/2014-05-14-qt-applications-desktop-mobiles-6.md b/blog/_posts/2014-05-14-qt-applications-desktop-mobiles-6.md similarity index 54% rename from site/content/posts/2014-05-14-qt-applications-desktop-mobiles-6.md rename to blog/_posts/2014-05-14-qt-applications-desktop-mobiles-6.md index 1b5b8d98a..5746a7336 100644 --- a/site/content/posts/2014-05-14-qt-applications-desktop-mobiles-6.md +++ b/blog/_posts/2014-05-14-qt-applications-desktop-mobiles-6.md @@ -4,7 +4,7 @@ author: Yves date: 2014-05-14T07:54:00+00:00 image: /img/2015-03-Sogilis-Christophe-Levet-Photographe-7461.jpg categories: - - DÉVELOPPEMENT + - Développement logiciel tags: - c++ - mobile @@ -16,26 +16,26 @@ Suite de la découverte de la programmation desktop et mobile avec Qt. ## Sommaire -- [L'application de base]({{< relref "posts/2014-04-29-qt-applications-desktop-mobiles-1.md#base-app" >}}) - - [Qt et application Qt Quick]({{< relref "posts/2014-04-29-qt-applications-desktop-mobiles-1.md#qt" >}}) - - [Prérequis]({{< relref "posts/2014-04-29-qt-applications-desktop-mobiles-1.md#req" >}}) - - [Créer un projet Qt Quick]({{< relref "posts/2014-04-29-qt-applications-desktop-mobiles-1.md#quick" >}}) - - [Découverte rapide]({{< relref "posts/2014-04-29-qt-applications-desktop-mobiles-1.md#discover" >}}) - - [2048]({{< relref "posts/2014-04-30-qt-applications-desktop-mobiles-2.md#2048" >}}) - - [2048.c]({{< relref "posts/2014-04-30-qt-applications-desktop-mobiles-2.md#c" >}}) - - [2048 en Qt]({{< relref "posts/2014-04-30-qt-applications-desktop-mobiles-2.md#qt" >}}) -- [Interface QML]({{< relref "posts/2014-05-06-qt-applications-desktop-mobiles-3.md#interface" >}}) - - [Board]({{< relref "posts/2014-05-06-qt-applications-desktop-mobiles-3.md#board" >}}) - - [Affichage du plateau]({{< relref "posts/2014-05-06-qt-applications-desktop-mobiles-3.md#display" >}}) - - [Un peu de style]({{< relref "posts/2014-05-06-qt-applications-desktop-mobiles-3.md#style" >}}) - - [Déplacement et jeu]({{< ref "posts/2014-05-07-qt-applications-desktop-mobiles-4.md" >}}) - - [Score et status]({{< ref "posts/2014-05-13-qt-applications-desktop-mobiles-5.md" >}}) - - [Responsive design]({{< ref "posts/2014-05-14-qt-applications-desktop-mobiles-6.md" >}}) -- [Et pour les mobiles]({{< relref "posts/2014-05-15-qt-applications-desktop-mobiles-7.md#mobile" >}}) - - [Gestures]({{< relref "posts/2014-05-15-qt-applications-desktop-mobiles-7.md#gestures" >}}) - - [iOS]({{< relref "posts/2014-05-15-qt-applications-desktop-mobiles-7.md#ios" >}}) - - [Android]({{< relref "posts/2014-05-15-qt-applications-desktop-mobiles-7.md#android" >}}) -- [Fin ?]({{< relref "posts/2014-05-15-qt-applications-desktop-mobiles-7.md#end" >}}) +- [L'application de base](./2014-04-29-qt-applications-desktop-mobiles-1.md#base-app) + - [Qt et application Qt Quick](./2014-04-29-qt-applications-desktop-mobiles-1.md#qt) + - [Prérequis](./2014-04-29-qt-applications-desktop-mobiles-1.md#req) + - [Créer un projet Qt Quick](./2014-04-29-qt-applications-desktop-mobiles-1.md#quick) + - [Découverte rapide](./2014-04-29-qt-applications-desktop-mobiles-1.md#discover) + - [2048](./2014-04-30-qt-applications-desktop-mobiles-2.md#2048) + - [2048.c](./2014-04-30-qt-applications-desktop-mobiles-2.md#c) + - [2048 en Qt](./2014-04-30-qt-applications-desktop-mobiles-2.md#qt) +- [Interface QML](./2014-05-06-qt-applications-desktop-mobiles-3.md#interface) + - [Board](./2014-05-06-qt-applications-desktop-mobiles-3.md#board) + - [Affichage du plateau](./2014-05-06-qt-applications-desktop-mobiles-3.md#display) + - [Un peu de style](./2014-05-06-qt-applications-desktop-mobiles-3.md#style) + - [Déplacement et jeu](./2014-05-07-qt-applications-desktop-mobiles-4.md) + - [Score et status](./2014-05-13-qt-applications-desktop-mobiles-5.md) + - [Responsive design](./2014-05-14-qt-applications-desktop-mobiles-6.md) +- [Et pour les mobiles](./2014-05-15-qt-applications-desktop-mobiles-7.md#mobile) + - [Gestures](./2014-05-15-qt-applications-desktop-mobiles-7.md#gestures) + - [iOS](./2014-05-15-qt-applications-desktop-mobiles-7.md#ios) + - [Android](./2014-05-15-qt-applications-desktop-mobiles-7.md#android) +- [Fin ?](./2014-05-15-qt-applications-desktop-mobiles-7.md#end) ### Responsive design @@ -49,15 +49,15 @@ De la même manière il est possible de remplacer les valeurs en dur (police, g Par exemple, pour faire varier la taille du texte du status et du score, remplaçons l'actuel. -{{< highlight cpp >}} +```cpp font.pointSize: 30 -{{< /highlight >}} +``` par -{{< highlight cpp >}} +```cpp font.pointSize: Math.min(main.width, main.height) / 12 -{{< /highlight >}} +``` Et pour que cela fonctionne, il faut rajouter `main` comme `id` à notre `Window`. @@ -65,7 +65,7 @@ Ainsi, lorsque la hauteur ou largeur de la fenêtre changera, la taille du texte Pour le plateau, nous allons faire quasiment la même chose : -{{< highlight cpp >}} +```cpp Rectangle { id: mainBoard Layout.fillHeight: true @@ -96,7 +96,7 @@ Rectangle { } } } -{{< /highlight >}} +``` Vous noterez juste que j'ai introduit un nouveau `Rectangle`. Celui-ci occupe toute la place disponible (via `Layout.fillWidth` et `Layout.fillHeight`) et permet surtout de très facilement centrer horizontalement le plateau. diff --git a/site/content/posts/2014-05-15-qt-applications-desktop-mobiles-7.md b/blog/_posts/2014-05-15-qt-applications-desktop-mobiles-7.md similarity index 80% rename from site/content/posts/2014-05-15-qt-applications-desktop-mobiles-7.md rename to blog/_posts/2014-05-15-qt-applications-desktop-mobiles-7.md index eea236a7a..3115c6492 100644 --- a/site/content/posts/2014-05-15-qt-applications-desktop-mobiles-7.md +++ b/blog/_posts/2014-05-15-qt-applications-desktop-mobiles-7.md @@ -4,7 +4,7 @@ author: Yves date: 2014-05-15T07:42:00+00:00 image: /img/2016-04-1.Design.jpg categories: - - DÉVELOPPEMENT + - Développement logiciel tags: - c++ - mobile @@ -16,26 +16,26 @@ Suite et fin de la découverte de la programmation desktop et mobile avec Qt. ## Sommaire -- [L'application de base]({{< relref "posts/2014-04-29-qt-applications-desktop-mobiles-1.md#base-app" >}}) - - [Qt et application Qt Quick]({{< relref "posts/2014-04-29-qt-applications-desktop-mobiles-1.md#qt" >}}) - - [Prérequis]({{< relref "posts/2014-04-29-qt-applications-desktop-mobiles-1.md#req" >}}) - - [Créer un projet Qt Quick]({{< relref "posts/2014-04-29-qt-applications-desktop-mobiles-1.md#quick" >}}) - - [Découverte rapide]({{< relref "posts/2014-04-29-qt-applications-desktop-mobiles-1.md#discover" >}}) - - [2048]({{< relref "posts/2014-04-30-qt-applications-desktop-mobiles-2.md#2048" >}}) - - [2048.c]({{< relref "posts/2014-04-30-qt-applications-desktop-mobiles-2.md#c" >}}) - - [2048 en Qt]({{< relref "posts/2014-04-30-qt-applications-desktop-mobiles-2.md#qt" >}}) -- [Interface QML]({{< relref "posts/2014-05-06-qt-applications-desktop-mobiles-3.md#interface" >}}) - - [Board]({{< relref "posts/2014-05-06-qt-applications-desktop-mobiles-3.md#board" >}}) - - [Affichage du plateau]({{< relref "posts/2014-05-06-qt-applications-desktop-mobiles-3.md#display" >}}) - - [Un peu de style]({{< relref "posts/2014-05-06-qt-applications-desktop-mobiles-3.md#style" >}}) - - [Déplacement et jeu]({{< ref "posts/2014-05-07-qt-applications-desktop-mobiles-4.md" >}}) - - [Score et status]({{< ref "posts/2014-05-13-qt-applications-desktop-mobiles-5.md" >}}) - - [Responsive design]({{< ref "posts/2014-05-14-qt-applications-desktop-mobiles-6.md" >}}) -- [Et pour les mobiles]({{< relref "posts/2014-05-15-qt-applications-desktop-mobiles-7.md#mobile" >}}) - - [Gestures]({{< relref "posts/2014-05-15-qt-applications-desktop-mobiles-7.md#gestures" >}}) - - [iOS]({{< relref "posts/2014-05-15-qt-applications-desktop-mobiles-7.md#ios" >}}) - - [Android]({{< relref "posts/2014-05-15-qt-applications-desktop-mobiles-7.md#android" >}}) -- [Fin ?]({{< relref "posts/2014-05-15-qt-applications-desktop-mobiles-7.md#end" >}}) +- [L'application de base](./2014-04-29-qt-applications-desktop-mobiles-1.md#base-app) + - [Qt et application Qt Quick](./2014-04-29-qt-applications-desktop-mobiles-1.md#qt) + - [Prérequis](./2014-04-29-qt-applications-desktop-mobiles-1.md#req) + - [Créer un projet Qt Quick](./2014-04-29-qt-applications-desktop-mobiles-1.md#quick) + - [Découverte rapide](./2014-04-29-qt-applications-desktop-mobiles-1.md#discover) + - [2048](./2014-04-30-qt-applications-desktop-mobiles-2.md#2048) + - [2048.c](./2014-04-30-qt-applications-desktop-mobiles-2.md#c) + - [2048 en Qt](./2014-04-30-qt-applications-desktop-mobiles-2.md#qt) +- [Interface QML](./2014-05-06-qt-applications-desktop-mobiles-3.md#interface) + - [Board](./2014-05-06-qt-applications-desktop-mobiles-3.md#board) + - [Affichage du plateau](./2014-05-06-qt-applications-desktop-mobiles-3.md#display) + - [Un peu de style](./2014-05-06-qt-applications-desktop-mobiles-3.md#style) + - [Déplacement et jeu](./2014-05-07-qt-applications-desktop-mobiles-4.md) + - [Score et status](./2014-05-13-qt-applications-desktop-mobiles-5.md) + - [Responsive design](./2014-05-14-qt-applications-desktop-mobiles-6.md) +- [Et pour les mobiles](./2014-05-15-qt-applications-desktop-mobiles-7.md#mobile) + - [Gestures](./2014-05-15-qt-applications-desktop-mobiles-7.md#gestures) + - [iOS](./2014-05-15-qt-applications-desktop-mobiles-7.md#ios) + - [Android](./2014-05-15-qt-applications-desktop-mobiles-7.md#android) +- [Fin ?](./2014-05-15-qt-applications-desktop-mobiles-7.md#end) ## Et pour les mobiles ? @@ -53,7 +53,7 @@ On va donc faire que cet objet recouvre toute notre application et traque les g Voici le code qui a été ajouté à la vue : -{{< highlight cpp >}} +```cpp property int gesture_swipeLeft: 0; property int gesture_swipeRight: 1; property int gesture_swipeUp: 2; @@ -124,7 +124,7 @@ MouseArea { } } } -{{< /highlight >}} +``` Petite explication de texte. Lors de l'appui sur la zone je garde en mémoire les coordonnées. Lors du relachement je calcule l'action réalisée (est-ce qu'il s'agit d'un glissement suffisant, ici 25% de l'écran, et si oui dans quelle direction). @@ -184,9 +184,9 @@ Il va vous proposer les simulateurs ou matériels android connectés corresponda Si jamais les devices que vous voyez sont tous en non compatible c'est probablement que vous n'êtes pas en Qt Creator 3.1.0. Dans ce cas, dans l’_environnement de compilation_ du kit rajoutez la variable d'environnement suivante : -{{< highlight cpp >}} +```cpp ANDROID_TARGET_ARCH=default/armeabi-v7a -{{< /highlight >}} +``` En effet, dans les SDK précédents l'architecture cible était `armeabi-v7a`, désormais c'est `default/armeabi-v7a` et les anciennes versions de Qt Creator se trompaient donc dans le test. diff --git a/site/content/posts/2014-05-22-videos-sogilis-salaries.md b/blog/_posts/2014-05-22-videos-sogilis-salaries.md similarity index 75% rename from site/content/posts/2014-05-22-videos-sogilis-salaries.md rename to blog/_posts/2014-05-22-videos-sogilis-salaries.md index f9ddc4cd0..e8ab5c7fd 100644 --- a/site/content/posts/2014-05-22-videos-sogilis-salaries.md +++ b/blog/_posts/2014-05-22-videos-sogilis-salaries.md @@ -1,18 +1,18 @@ --- -title: "Vidéos : Sogilis, vu par ses salariés" +title: 'Vidéos : Sogilis, vu par ses salariés' author: Tiphaine date: 2014-05-22T07:33:00+00:00 image: /img/2014-05-Sogilis-Christophe-Levet-Photographe-8389.jpg categories: - - NOTRE VISION + - Notre vision tags: - amélioration continue - bonheur au travail - entreprise - management - recrutement - --- + Il y a quelques temps les équipes ont étés filmées pendant une journée dans les bureaux de Sogilis. Certains des membres de l'équipe ont parlé de Sogilis devant la caméra. La première vidéo présente la société et son mode de fonctionnement atypique. @@ -23,13 +23,16 @@ La manière dont se passe le recrutement chez Sogilis est également disruptive. **Les entretiens individuels se font tous les 4 mois**, ce qui permet d'avoir une boucle de rétroaction courte et d'avoir un moment de dialogue privilégié avec les équipes dirigeantes pour faire le point et avoir des pistes d'amélioration personnelle. -{{< youtube 3dTQHLqrGRA >}} + + + Dans cette deuxième vidéo les équipes donnent leur avis sur le [mode d’organisation cellulaire](https://blog.sogilis.com/posts/2014-03-11-management-sans-manager-sogilis/) de l’entreprise. - Les salariés parlent aussi de la **liberté des équipes dans leur travail** et de leur volonté d’**amélioration continue**. -{{< youtube qaMI7SlFnoQ >}} + + + Pour voir toutes les vidéos de Sogilis, [c’est par ici](https://www.youtube.com/user/SogilisFR). diff --git a/site/content/posts/2014-05-28-tcp-nat-traversal-nodejs-stun.md b/blog/_posts/2014-05-28-tcp-nat-traversal-nodejs-stun.md similarity index 79% rename from site/content/posts/2014-05-28-tcp-nat-traversal-nodejs-stun.md rename to blog/_posts/2014-05-28-tcp-nat-traversal-nodejs-stun.md index 7b2c9d870..36aa61b1b 100644 --- a/site/content/posts/2014-05-28-tcp-nat-traversal-nodejs-stun.md +++ b/blog/_posts/2014-05-28-tcp-nat-traversal-nodejs-stun.md @@ -4,13 +4,13 @@ author: Shanti date: 2014-05-28T07:57:00+00:00 image: /img/2016-04-2.Produits.jpg categories: - - DÉVELOPPEMENT + - Développement logiciel tags: - code - développement - Nodejs - --- + With the scarecity of IPv4 addresses, and IPv6 still not available at large, NAT traversal is becoming a necessity. Especially with the generalisation of Carrier-grade NATs that you can find on mobile connections. Even with IPv6 you may suffer NAT66. Imagine your mobile device that gets only a single Ipv6 address, and you want to share it on your computer. The solution might be in a decentralized protocol for address attribution such as cjdns. In cjdns, addresses are private cryptographic keys. Anyone can generate his or her own address at will. This requires however a change in the routing protocols. @@ -25,13 +25,13 @@ A UDP socket is a file descriptor that is bound locally on your computer to a l This is how UDP sockets are set up using unix system calls: -* `socket(AF_INET, SOCK_DGRAM, 0)`: create the file descriptor -* `bind(fd, addr, len)`: bind the socket to an address and port on your computer +- `socket(AF_INET, SOCK_DGRAM, 0)`: create the file descriptor +- `bind(fd, addr, len)`: bind the socket to an address and port on your computer And now, your socket is ready to receive and send messages. This is done with: -* `sendto(fd, payload, len, flags, dest_addr, addr_len)`: Send a packet to the specified address. -* `recvfrom(fd, payload, len, flags, src_addr, addr_len)`: Fetch the received packet along with the address it comes from. +- `sendto(fd, payload, len, flags, dest_addr, addr_len)`: Send a packet to the specified address. +- `recvfrom(fd, payload, len, flags, src_addr, addr_len)`: Fetch the received packet along with the address it comes from. ## TCP Sockets @@ -47,14 +47,14 @@ For UDP, incoming packets are sorted using the destination address and port num Let’s see the system calls required to create a simple TCP connecton: -* `socket(AF_INET, SOCK_STREAM, 0)`: creates the file descriptor -* `bind(fd, addr, len)`: bind the socket to an address and port on your computer. Optional in case of TCP (a source port will be chosen automatically at random) -* `connect(fd, addr, len)`: connect the socket to a destination IP and port. +- `socket(AF_INET, SOCK_STREAM, 0)`: creates the file descriptor +- `bind(fd, addr, len)`: bind the socket to an address and port on your computer. Optional in case of TCP (a source port will be chosen automatically at random) +- `connect(fd, addr, len)`: connect the socket to a destination IP and port. As we can see, there is now a new system call, `connect`, that is used to tell the kernel the destination IP and port. Then data can be transmitted using: -* `send(fd, buffer, len, flags)` -* `recv(fd, buffer, len, flags)` +- `send(fd, buffer, len, flags)` +- `recv(fd, buffer, len, flags)` Note that contrary to UDP, the `send` and `recv` system calls don’t take an address as it has already been given during `connect`. @@ -62,10 +62,10 @@ Note that contrary to UDP, the `send` and `recv` system calls don’t take an a What if you want to have multiple connections on the same port of your computer ? You’ll first have to add the option `SO_REUSEADDR` to the existing TCP sockets on the same port to tell them they are not exclusive. Then, any number of sockets can be bound on the same port. The system call is: -{{< highlight js >}} +```js int so_reuseaddr = TRUE; setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &so_reuseaddr, sizeof so_reuseaddr); -{{< /highlight >}} +``` Also, to have multiple sockets, they must be bound on a specific local address, and not on the default address `0.0.0.0` (which will receive packets from any interface). @@ -73,15 +73,15 @@ Also, to have multiple sockets, they must be bound on a specific local address, Now, using TCP you can also set up the special _listening_ socket. This is how it is done: -* `socket(AF_INET, SOCK_STREAM, 0)`: creates the file descriptor -* `bind(fd, addr, len)`: bind the socket to an address and port on your computer. This is no longer optional as we do not create a connection. -* `listen(fd, backlog)`: mark the socket as the listening socketfor the local address and port. Any packet not fitting in an existing connection will be associated to this socket. +- `socket(AF_INET, SOCK_STREAM, 0)`: creates the file descriptor +- `bind(fd, addr, len)`: bind the socket to an address and port on your computer. This is no longer optional as we do not create a connection. +- `listen(fd, backlog)`: mark the socket as the listening socketfor the local address and port. Any packet not fitting in an existing connection will be associated to this socket. When you have a listening socket on your local address, you an no longer create connection sockets directly. You must wait for the other end to contact your listening socket that will then spawn file descriptors for each connection that is arriving. This is done using: -{{< highlight js >}} -accept(fd, src_addr, addr_len, flags) -{{< /highlight >}} +```js +accept(fd, src_addr, addr_len, flags); +``` When a packet comes in and it is not put into any existing connection, the `accept` system call will create a new connection for that sender and return a connected socket as return value of `accept`. Then, `send` and `recv` can be used on that new socket. @@ -97,16 +97,16 @@ When the reply comes back to the translator, it remembers using the port number Consequences: -* By relying on associating a private IP address with a port on the translator, it won’t work with protocols below TCP or UDP. new protocols over IP won’t be accessible to the private network. -* It doesn’t work if all the hosts of the private network make use of all of their 65536 ports. The translator just won’t have enough ports to keep track of all the connections. -* And of course, the hosts on the private network are not directly reachable. +- By relying on associating a private IP address with a port on the translator, it won’t work with protocols below TCP or UDP. new protocols over IP won’t be accessible to the private network. +- It doesn’t work if all the hosts of the private network make use of all of their 65536 ports. The translator just won’t have enough ports to keep track of all the connections. +- And of course, the hosts on the private network are not directly reachable. ## UDP Hole Punching UDP hole punching is a simple algorithm to get through NATs. And this is where the STUN server might be of some help. The idea is as follows: -* Send the STUN server a UDP query -* The STUN server replies with your public IP address and port. Behind a NAT, this is the IP address of the NAT and the port the NAT has chosen to associate to your host in the private network. +- Send the STUN server a UDP query +- The STUN server replies with your public IP address and port. Behind a NAT, this is the IP address of the NAT and the port the NAT has chosen to associate to your host in the private network. In most configuration this is enough to get anyone else contacting you using UDP through the IP address and port you have been given. The NAT will probably forward them. it is not guaranteed though because when someone else than the STUN server tries to contact you, the source IP and port will be different, and the NAT might check them too. @@ -118,10 +118,10 @@ You will also need to keep some traffic running on that port, else the NAT might On TCP, the idea is quite the same with the additional difficulty that you must keep the connection open to the STUN server, and listen on the same port for incoming connections. As we saw, this is possible using `SO_REUSEADDR`. However, the connection to the STUN server must be initiated first before setting up the listening socket. This is how it works: -* Connect to the STUN server, and get your public IP and port -* Set up the listening socket on the same local port as you are contacting the STUN server -* Tell your peers that cou can be contacted through your public IP and port and hope the NAT will forward the requests to you. -* Never disconnect from the STUN server, else the NAT might close your redirection. +- Connect to the STUN server, and get your public IP and port +- Set up the listening socket on the same local port as you are contacting the STUN server +- Tell your peers that cou can be contacted through your public IP and port and hope the NAT will forward the requests to you. +- Never disconnect from the STUN server, else the NAT might close your redirection. The success rate is somewhat lower with this method because NAT generally track more things about the TCP connections than UDP. @@ -139,71 +139,79 @@ To this day, I haven’t got it to work. Mostly because I haven’t found a suit I crafted a `PublicTCP` object: -{{< highlight js >}} +```js var shortenSTUN = true; // set to true if your STUN server disconnects too early var localIP = '192.168.0.x'; // You can try 0.0.0.0 -var net = require('net'); -var stun = require('vs-stun'); +var net = require('net'); +var stun = require('vs-stun'); var events = require('events'); -var PublicTCP = function(cb){ - if(cb) this.on('refresh', cb); -} +var PublicTCP = function (cb) { + if (cb) this.on('refresh', cb); +}; -PublicTCP.prototype = new events.EventEmitter; +PublicTCP.prototype = new events.EventEmitter(); -PublicTCP.prototype.start = function(stunserver){ // example: stun.stunprotocol.org - if(typeof stunserver === 'string') stunserver = {host: stunserver}; +PublicTCP.prototype.start = function (stunserver) { + // example: stun.stunprotocol.org + if (typeof stunserver === 'string') stunserver = { host: stunserver }; stunserver.port = stunserver.port || 3478; var self = this; - var sock = this.socket = net.connect({port: stunserver.port, host: stunserver.host, localAddress: localIP}, - function(){ - stun.resolve_tcp(sock, stunserver, function(err, value) { - if (err) { - console.log('Something went wrong: ' + err); - } else { - console.log("STUN Response:") - console.log(value); - self.emit('refresh', value); - } - }, {short: shortenSTUN}); - }); -} - -PublicTCP.prototype.close = function(){ + var sock = (this.socket = net.connect( + { port: stunserver.port, host: stunserver.host, localAddress: localIP }, + function () { + stun.resolve_tcp( + sock, + stunserver, + function (err, value) { + if (err) { + console.log('Something went wrong: ' + err); + } else { + console.log('STUN Response:'); + console.log(value); + self.emit('refresh', value); + } + }, + { short: shortenSTUN } + ); + } + )); +}; + +PublicTCP.prototype.close = function () { this.emit('close'); this.socket.end(); -} -{{< /highlight >}} +}; +``` This `PublicIP` object will connect to a STUN server when issued `start(stunserver)` and will emit the `refresh` event with the STUN response to signal the listening socket can be started. There are two quirks in that code: -* The first (`shortenSTUN`) that we already saw is used to avoid checking every detail of the NAT and only reply with the first answer. This doesn’t help however because the STUN connection is closed and the NAT is likely removing the binding from its translation table. -* Then, when using `net.connect` we specify `localAddress` to bind to the local address of the computer. If not specified it defults to `0.0.0.0` which binds to every interface. However binding the socket to every interface prevent creating the listening socket later on with the same port. +- The first (`shortenSTUN`) that we already saw is used to avoid checking every detail of the NAT and only reply with the first answer. This doesn’t help however because the STUN connection is closed and the NAT is likely removing the binding from its translation table. +- Then, when using `net.connect` we specify `localAddress` to bind to the local address of the computer. If not specified it defults to `0.0.0.0` which binds to every interface. However binding the socket to every interface prevent creating the listening socket later on with the same port. This is and example of how this prototype can be used: -{{< highlight js >}} +```js var tcp = new PublicTCP(); -tcp.on('refresh', function(data){ - sock = net.createServer(function(c){}); +tcp.on('refresh', function (data) { + sock = net.createServer(function (c) {}); sock.listen(data.local.port, data.local.host); - console.log("Local listening socket:"); + console.log('Local listening socket:'); console.log(sock.address()); - setTimeout(function(){ - console.log("timeout"); + setTimeout(function () { + console.log('timeout'); tcp.close(); }, 5000); - tcp.on('close', function(){ + tcp.on('close', function () { sock.close(); }); }); -tcp.start("stun.stunprotocol.org"); -{{< /highlight >}} +tcp.start('stun.stunprotocol.org'); +``` ## Conclusion @@ -213,6 +221,6 @@ If you want to host this kind of server in web providers, beware that they might ## References -* [Peer-to-Peer Communication Across Network Address Translators](http://www.brynosaurus.com/pub/net/p2pnat/) -* [RFC 5389](https://tools.ietf.org/html/rfc5389) [and the obsolete (RFC 3489](https://tools.ietf.org/html/rfc3489)) -* [draft-ietf-behave-nat-behavior-discovery-02](http://wiki.tools.ietf.org/html/draft-ietf-behave-nat-behavior-discovery-02) that has some bits from RFC 3489 not included in RFC 5389. +- [Peer-to-Peer Communication Across Network Address Translators](http://www.brynosaurus.com/pub/net/p2pnat/) +- [RFC 5389](https://tools.ietf.org/html/rfc5389) [and the obsolete (RFC 3489](https://tools.ietf.org/html/rfc3489)) +- [draft-ietf-behave-nat-behavior-discovery-02](http://wiki.tools.ietf.org/html/draft-ietf-behave-nat-behavior-discovery-02) that has some bits from RFC 3489 not included in RFC 5389. diff --git a/site/content/posts/2014-07-08-marshmallow-challenge.md b/blog/_posts/2014-07-08-marshmallow-challenge.md similarity index 84% rename from site/content/posts/2014-07-08-marshmallow-challenge.md rename to blog/_posts/2014-07-08-marshmallow-challenge.md index 7a023c280..4854e2c68 100644 --- a/site/content/posts/2014-07-08-marshmallow-challenge.md +++ b/blog/_posts/2014-07-08-marshmallow-challenge.md @@ -4,12 +4,12 @@ author: Luc Jeanniard date: 2014-07-08T07:25:00+00:00 image: /img/2016-04-1.Produits.jpg categories: - - DÉVELOPPEMENT + - Développement logiciel tags: - agile - --- -**Depuis quelques mois j'accompagne l'équipe de développement de Magellan, un éditeur de solutions de gestion des transactions électroniques sécurisées, pour faciliter la transition d'une gestion de projet classique Waterfall vers une gestion de projet Agile. L'objectif est que l'entreprise ait plus de visibilité sur les projets en cours et ainsi faciliter le pilotage. L'agilité nécessite une forte cohésion d'équipe. La notion d'équipe et la communication sont des sujets de tous les instants et les rétrospectives y reviennent très souvent. + +Depuis quelques mois j'accompagne l'équipe de développement de Magellan, un éditeur de solutions de gestion des transactions électroniques sécurisées, pour faciliter la transition d'une gestion de projet classique Waterfall vers une gestion de projet Agile. L'objectif est que l'entreprise ait plus de visibilité sur les projets en cours et ainsi faciliter le pilotage. L'agilité nécessite une forte cohésion d'équipe. La notion d'équipe et la communication sont des sujets de tous les instants et les rétrospectives y reviennent très souvent. Arrivant au terme d'une partie de l'accompagnement avec la mise en place de Scrum et de Kanban, j'ai eu l'envie de faire un jeu pour illustrer l'importance de la communication dans les équipes et la puissance des méthodes itératives. J'ai choisi d'organiser le célèbre Marshmallow Challenge ! @@ -33,7 +33,9 @@ David, le responsable des équipes était missionné pour trouver un petit cadea J'avais prévu un peu de musique pour l'ambiance. J'avais choisi Toto, c'est rock et entraînant ! -{{< youtube htgr3pvBr-I >}} + + + Tout le monde était convié mais ne savait pas pourquoi :). Le secret a été bien gardé pour une surprise maximale. diff --git a/site/content/posts/2014-08-12-how-deploy-services-docker.md b/blog/_posts/2014-08-12-how-deploy-services-docker.md similarity index 83% rename from site/content/posts/2014-08-12-how-deploy-services-docker.md rename to blog/_posts/2014-08-12-how-deploy-services-docker.md index 37506f67f..1b21682b0 100644 --- a/site/content/posts/2014-08-12-how-deploy-services-docker.md +++ b/blog/_posts/2014-08-12-how-deploy-services-docker.md @@ -4,14 +4,14 @@ author: Shanti date: 2014-08-12T09:10:00+00:00 image: /img/2014-08-Sogilis-Christophe-Levet-Photographe-7517.jpg categories: - - DÉVELOPPEMENT + - Développement logiciel tags: - debian - deployment - docker - en - --- + Why would you use docker to deploy services when there are many configuration management tools such as puppet, chef or cfengine. While these tools are a great evolution from doing everything manually, they are not separating clearly between the configuration of your system (your domain, user accounts) and the installation and integration of services together. Docker is a container solution. You manage your services as binary images (built from a `Dockerfile` script). The idea is that container images won’t contain configuration that is too specific. Regenerating whole binary images if you want to flip a boolean value is not a good idea. You’ll have then to think how you can build generic images and put your configuration outside. @@ -28,33 +28,33 @@ When you run these services you need both static configuration that will never c Static configuration is bundled within the docker image, and is specified in the `Dockerfile`, the recipe that builds the images. It includes: -* package installation -* socket configuration for communication between dovecot (the IMAP server) and exim (the SMTP/submission server) -* default configuration for both SMTP and IMAP -* virtual host configuration for the webmail +- package installation +- socket configuration for communication between dovecot (the IMAP server) and exim (the SMTP/submission server) +- default configuration for both SMTP and IMAP +- virtual host configuration for the webmail ### Dynamic configuration Dynamic configuration is a little more complicated. I believe it should be minimal and most of the choices should be bundled within the disk image itself. What you are likely to find here is: -* domain names for which the mail server accepts mail for -* domain names for which the mail server is relaying mail -* user database (the choice was to have a passwd-like file containing the user database, but a more complete setup would have this changed to connect to another container providing authentication such as a LDAP server or a database) -* address of the mail server for the webmail +- domain names for which the mail server accepts mail for +- domain names for which the mail server is relaying mail +- user database (the choice was to have a passwd-like file containing the user database, but a more complete setup would have this changed to connect to another container providing authentication such as a LDAP server or a database) +- address of the mail server for the webmail Then, how can dynamic configuration can be specified? Docker provides a few tools: -* command line arguments when starting the container -* environment variables set on the container -* linked containers. A container (the webmail) can be linked to another (the mail server). The network interfaces of the mail server will be exposed within the webmail container. The hostname and ports of the linked containers are found in environment variables and the `/etc/hosts` file. -* when starting a container, docker can run a specified file (generally a shell script) that use the information above to configure the container at startup. +- command line arguments when starting the container +- environment variables set on the container +- linked containers. A container (the webmail) can be linked to another (the mail server). The network interfaces of the mail server will be exposed within the webmail container. The hostname and ports of the linked containers are found in environment variables and the `/etc/hosts` file. +- when starting a container, docker can run a specified file (generally a shell script) that use the information above to configure the container at startup. This works well for everything except for the user file list. Notice that if an authentication container existed, we could link it into the mail container and it would work very well. In that case, we’ll see later that we can setup a ssh access to enter the container. Once inside, we can add a user using standard unix commands. The code: -* [docker-mail on github](https://github.com/mildred/docker-mail) -* [docker-roundcube on github](https://github.com/mildred/docker-roundcube) +- [docker-mail on github](https://github.com/mildred/docker-mail) +- [docker-roundcube on github](https://github.com/mildred/docker-roundcube) ## How to have docker containers always running @@ -68,39 +68,39 @@ There is a tool called [geard](https://openshift.github.io/geard/) from the Open The JSON deployment file looks like: -{{< highlight json >}} +```json { - "Keys":[{ - "Type": "authorized_keys", - "Value": "ssh-rsa ..." - }], - "containers":[ + "Keys": [ + { + "Type": "authorized_keys", + "Value": "ssh-rsa ..." + } + ], + "containers": [ { - "name":"mail", - "count":1, - "image":"mildred/exim-dovecot-mail", - "publicports":[ - {"internal":4190, "external":4190}, - {"internal":25, "external":25}, - {"internal":993, "external":993}, - {"internal":143, "external":143}, - {"internal":465, "external":465}, - {"internal":587, "external":587} + "name": "mail", + "count": 1, + "image": "mildred/exim-dovecot-mail", + "publicports": [ + { "internal": 4190, "external": 4190 }, + { "internal": 25, "external": 25 }, + { "internal": 993, "external": 993 }, + { "internal": 143, "external": 143 }, + { "internal": 465, "external": 465 }, + { "internal": 587, "external": 587 } ], - "environment": [ - {"name": "LOCAL_DOMAINS", "value": "example.org"} - ] + "environment": [{ "name": "LOCAL_DOMAINS", "value": "example.org" }] }, { - "name":"roundcube", - "count":1, - "image":"mildred/roundcube", - "links":[{"to":"mail"}], - "publicports":[{"internal":443, "external":4443}] + "name": "roundcube", + "count": 1, + "image": "mildred/roundcube", + "links": [{ "to": "mail" }], + "publicports": [{ "internal": 443, "external": 4443 }] } ] } -{{< /highlight >}} +``` Moreover, it lets you specify ssh keys for your containers, and set up the ssh daemon so these ssh keys can let you in the containers directly. `ssh container-name@host` will provide you a shell on the container `container-name` running on `host`. @@ -112,7 +112,7 @@ On top of this, the deployment JSON file is not documented anywhere. Gear has a great way to do this, we could always do the same things manually, in a declarative format. To simplify things the most, I came up with a shell script that I run on the server and that does the same provisioning as the JSON file above: -{{< highlight bash >}} +```bash name=example LOCAL_DOMAINS=example.org ssh_key="ssh-rsa ..." @@ -130,32 +130,34 @@ systemctl enable $name-mail systemctl enable $name-roundcube systemctl start $name-mail systemctl start $name-roundcube -{{< /highlight >}} +``` There is however a few things to execute first and some shell functions to define. However I like this solution the most because the configuration (the three variables at the top) is separated from the code that run the deployment. How is that implemented? Following [host integration](https://docs.docker.com/config/containers/start-containers-automatically/) you have to ensure a few things on your host system: -* that you are running systemd: - {{< highlight systemd >}} +- that you are running systemd: + + ``` DEBIAN_FRONTEND=noninteractive apt-get install -y systemd systemd-sysv - {{< /highlight >}} + ``` + +- that docker will not try to restart containers: -* that docker will not try to restart containers: - {{< highlight bash >}} + ```bash echo 'DOCKER_OPTS="-r=false"' > /etc/default/docker - {{< /highlight >}} + ``` -* and that you have the `nsenter` command to get a shell within a running container: - {{< highlight bash >}} +- and that you have the `nsenter` command to get a shell within a running container: + ```bash docker run --rm -v /usr/local/bin:/target jpetazzo/nsenter - {{< /highlight >}} + ``` #### Instantiate a container Once you have all that, let’s see how these shell functions are implemented. The function `docker-unit NAME ARGS` is there to declare a new instance of a container. It takes the instance name (`NAME`) and the docker arguments (`ARGS`) given to `docker run`. Its job is to create a systemd unit file, and that’s what it does literally: -{{< highlight systemd >}} +``` docker-unit(){ local name="$1" shift @@ -174,11 +176,11 @@ ExecStop=/usr/bin/docker stop -t 2 $name WantedBy=local.target EOF } -{{< /highlight >}} +``` You can see that the `ExecStart` command is running `/usr/local/bin/docker-start-run`. This file is a shell script that spawn the docker container, ensuring that the volumes are from another container `$name-data` (so they can be backuped easily). Its code is: -{{< highlight bash >}} +```bash #!/bin/bash name="$1" @@ -190,7 +192,7 @@ fi /usr/bin/docker rm "$name" exec /usr/bin/docker run --rm --attach=stdout --attach=stderr --name="$name" --volumes-from="$name-data" "$@" -{{< /highlight >}} +``` This could actually be inserted within the unit file (and that'w what gear does). From this code you can see that the container is recreated anew each time the service is restarted. The container must be designed such as all persistent data is declared in a volume. This persistent data will be put in the `*-data` container and it is the only thing that persists. @@ -204,6 +206,6 @@ What I did instead is create a user on the system with uid 0 for each container And this is how `docker-ssh` is implemented: -{{< highlight docker >}} +```docker docker-ssh(){ local user="$1" local name="$1" local shell="/bin/bash" shift while true; do case "$1" in -u*) user="${1#-?}" ;; -s*) shell="${1#-?}" ;; --user=*) user="${1#-*=}" ;; --shell=*) shell="${1#-*=}" ;; *) break ;; esac shift done [ "${shell////}" = "$shell" ] && shell="/bin/$shell" local ssh_key="$*" if uid="$(id -u "$user" 2>/dev/null)"; then if [ "0$uid" = 00 ]; then echo "Using existing user $user for ssh access" else echo "Existing user $user cannot be used for ssh access (uid $uid should be 0)" >&2 return 1 fi else echo "Creating user $user for ssh access" useradd -d /home/$user -N -m -o -u 0 $user fi mkdir -p /home/$user/.ssh chown -R $user /home/$user ( fgrep -v "$ssh_key" /home/$user/.ssh/authorized_keys 2>/dev/null echo "command="nsenter --target $(docker inspect --format {{.State.Pid}} $name) --mount --uts --ipc --net --pid $shell" $ssh_key" ) | sort | uniq >/home/$user/.ssh/authorized_keys- mv /home/$user/.ssh/authorized_keys- /home/$user/.ssh/authorized_keys } -{{< /highlight >}} +``` diff --git a/site/content/posts/2014-09-05-future-web-peer-peer.md b/blog/_posts/2014-09-05-future-web-peer-peer.md similarity index 86% rename from site/content/posts/2014-09-05-future-web-peer-peer.md rename to blog/_posts/2014-09-05-future-web-peer-peer.md index 0949c0c8b..51b618b4f 100644 --- a/site/content/posts/2014-09-05-future-web-peer-peer.md +++ b/blog/_posts/2014-09-05-future-web-peer-peer.md @@ -4,12 +4,12 @@ author: Sogilis date: 2014-09-05T12:26:00+00:00 image: /img/2016-04-2.Produits.jpg categories: - - DÉVELOPPEMENT + - Développement logiciel tags: - développement - peertopeer - --- + ## A little bit of History We got far away from the original ideas that founded the Internet. At first, it was a network of computers. Each computer on the network was given an IP address, and all computers were connected freely and were all equal in rights. Computers were not personal computers then. They were in places like universities and were online most of the time. They were always available for communication. @@ -26,12 +26,12 @@ There were other servers as well, such as web servers. They hosted the web pages Then we got the DSL. bandwidth was much more important, and more importantly, computers could be connected all the time at their leisure. They could get back all the services they delegated to other computers. But they did not. Why? -* because personal computers became lazy. Why do something we can let another computer do instead of us? -* because they are still not powered on all the time and protocols were not designed for that -* because the bandwidth, while greater, might still not be enough -* and also because they soon were not able to talk to other computers freely. Something called NAT was invented, and computers were forced to talk through a device that censored what they said or what the other computer they wanted to talk to said. In particular: - * the computers were then forced to talk either TCP or UDP, and no other language was accepted by the device - * the personal computer could only ask a question and receive a response. The device censored any computer trying to contact the personal computer directly +- because personal computers became lazy. Why do something we can let another computer do instead of us? +- because they are still not powered on all the time and protocols were not designed for that +- because the bandwidth, while greater, might still not be enough +- and also because they soon were not able to talk to other computers freely. Something called NAT was invented, and computers were forced to talk through a device that censored what they said or what the other computer they wanted to talk to said. In particular: + - the computers were then forced to talk either TCP or UDP, and no other language was accepted by the device + - the personal computer could only ask a question and receive a response. The device censored any computer trying to contact the personal computer directly The personal computer did not fight back. After all, this as all for very good reasons. @@ -39,10 +39,10 @@ The personal computer did not fight back. After all, this as all for very good  It is time to restore the personal computer’s constitutional rights: “all computers are connected freely and are all equal in rights”. We can do that by finding ways for computers to communicate with each other that bypass the restrictions that might be imposed on personal computers. Personal computers will then have an equal position on the community than any other computer. -* It should be easy to maintain to counteract laziness -* It should continue to work in perfect condition even when one computer goes asleep (also known as powering it down) -* It shouldn’t consume too much bandwidth so it stays accessible to slow links -* It should use many tricks to bypass the nasty device called NAT +- It should be easy to maintain to counteract laziness +- It should continue to work in perfect condition even when one computer goes asleep (also known as powering it down) +- It shouldn’t consume too much bandwidth so it stays accessible to slow links +- It should use many tricks to bypass the nasty device called NAT ## Empowering the human society @@ -52,24 +52,24 @@ Internet is a tool that makes us grow, think, interact. It’s a new stage in Hi What should we be able to do with the web? -* Be able to produce content without having to ask a third party (that could be pressured to censor what you want to say) -* Be able to view content produced by anyone. -* Be relatively anonymous (complete anonymity is quite impossible). Have no easy central place that can track what you are doing. +- Be able to produce content without having to ask a third party (that could be pressured to censor what you want to say) +- Be able to view content produced by anyone. +- Be relatively anonymous (complete anonymity is quite impossible). Have no easy central place that can track what you are doing. ## Introducing BitWeb There is already an interesting protocol out there that provides many of the things we need. It’s called BitTorrent. Can we use it to run the web? -* It can be installed as a software that is very easy to run on any computer -* It can bypass the nasty NAT device -* If a computer stops working, the content is always available in many other places -* It can share content at great speeds because content comes from many different places +- It can be installed as a software that is very easy to run on any computer +- It can bypass the nasty NAT device +- If a computer stops working, the content is always available in many other places +- It can share content at great speeds because content comes from many different places That solves the personal computer problems. What about empowering us, humans ? -* anyone can publish content. Using trackerless (or DHT) torrents, it is not necessary to have an intermediate to share the content for us -* it is possible to download any content. None is in the power to decide that you cannot download a torrent. You always can, and if it’s illegal, you might be tracked for it (but that’s just normal police work, or it should) -* there is no central place when using trackerless torrents. None knows exactly what you did. +- anyone can publish content. Using trackerless (or DHT) torrents, it is not necessary to have an intermediate to share the content for us +- it is possible to download any content. None is in the power to decide that you cannot download a torrent. You always can, and if it’s illegal, you might be tracked for it (but that’s just normal police work, or it should) +- there is no central place when using trackerless torrents. None knows exactly what you did. ## How can we replace the web with it @@ -125,8 +125,8 @@ Unfortunately, this is impossible. Even the anonymity networks such as Tor or I2 What we can easily do is ensure there is no central point that knows what you are doing when you visit a website, and that there is no global registry where this information can be found. -* When you visit a website, you have do download it. Those you download from will know what you downloaded and there is no way around that apart from using anonymity networks. -* The software must not share the parts it downloaded until you decided you wanted to participate in distributing the website. This will add you in the global DHT registry and anyone knows that ou have this site, probably because you looked at it. +- When you visit a website, you have do download it. Those you download from will know what you downloaded and there is no way around that apart from using anonymity networks. +- The software must not share the parts it downloaded until you decided you wanted to participate in distributing the website. This will add you in the global DHT registry and anyone knows that ou have this site, probably because you looked at it. If you want increased anonymity, you’ll have to use the same strategy as the anonymity networks: ask a neighbor to ask a neighbor to download the piece for you. @@ -134,11 +134,11 @@ If you want increased anonymity, you’ll have to use the same strategy as the a We get a protocol that: -* enables anyone to put a site online with no added costs -* is completely decentralized. No censorship is easily possible. -* has no central point to track what you are doing -* allow fast downloads by downloading from multiple peers at once -* allow some interactive behavior using JavaScript +- enables anyone to put a site online with no added costs +- is completely decentralized. No censorship is easily possible. +- has no central point to track what you are doing +- allow fast downloads by downloading from multiple peers at once +- allow some interactive behavior using JavaScript This is being developped on [github](https://github.com/mildred/bitweb) but the main job for the moment is to agree on an extension for mutable torrents. This has to take place on the [BitTorrent mailing list](http://bittorrent.org/mailing_list.html) (which seems to have problems) diff --git a/site/content/posts/2014-10-24-drone-hexo-hollande.md b/blog/_posts/2014-10-24-drone-hexo-hollande.md similarity index 99% rename from site/content/posts/2014-10-24-drone-hexo-hollande.md rename to blog/_posts/2014-10-24-drone-hexo-hollande.md index 440c89998..3c3c1ce6a 100644 --- a/site/content/posts/2014-10-24-drone-hexo-hollande.md +++ b/blog/_posts/2014-10-24-drone-hexo-hollande.md @@ -4,12 +4,12 @@ author: Tiphaine date: 2014-10-24T08:35:00+00:00 image: /img/2014-10-Sogilis-Christophe-Levet-Photographe-7651.jpg categories: - - LA VIE À SOGILIS + - La vie à sogilis tags: - Hexo+ - start-up - --- + Mardi avait lieu l’inauguration de la Halle Freyssinet, destiné à être le plus grand incubateur de start-up au monde. Christophe Baillon a profité de cet événement pour présenter la jeune entreprise Squadrone System, qui conçoit le drone Hexo+, à François Hollande, introduit par Xavier Niel. ![](https://66.media.tumblr.com/b33c5f53ccd13c49eef5bda5cfc403e8/tumblr_inline_ndxw0reE4V1t2p7ex.jpg) diff --git a/site/content/posts/2014-11-12-journee-accompagnement-business.md b/blog/_posts/2014-11-12-journee-accompagnement-business.md similarity index 85% rename from site/content/posts/2014-11-12-journee-accompagnement-business.md rename to blog/_posts/2014-11-12-journee-accompagnement-business.md index 2c236a41b..ad9323ac4 100644 --- a/site/content/posts/2014-11-12-journee-accompagnement-business.md +++ b/blog/_posts/2014-11-12-journee-accompagnement-business.md @@ -4,12 +4,12 @@ author: Tiphaine date: 2014-11-12T12:54:00+00:00 image: /img/2016-04-2.CS_BUSINESS_BasPage.jpg categories: - - NOTRE VISION + - Notre vision tags: - accompagnement - business - --- + Après le succès du projet Hexo+ avec un Kickstarter à 1.3 M$, Sogilis fait profiter aux porteurs de projets lyonnais d’une demi-journée d'accompagnement au design de leur Business Model. ![](https://66.media.tumblr.com/67e03cb7915aac32dc12f34f18dd4289/tumblr_inline_nexfoerc9H1t2p7ex.jpg) @@ -20,12 +20,12 @@ Après une courte présentation des projets, Laurent Mangue et Xavier Spengler, **Vous êtes :** -* porteur de projet ou dirigeant d’une société -* disponible à Lyon le 4 décembre de 14h à 18h +- porteur de projet ou dirigeant d’une société +- disponible à Lyon le 4 décembre de 14h à 18h **Vous souhaiteriez :** -* recevoir des conseils personnalisés afin d’accélérer votre accès aux premiers revenus et à l'adéquation produit/marché -* designer votre business model par la mise en place de tests itératifs +- recevoir des conseils personnalisés afin d’accélérer votre accès aux premiers revenus et à l'adéquation produit/marché +- designer votre business model par la mise en place de tests itératifs **Veuillez remplir ce formulaire afin d’être sélectionné parmi les porteurs de projet.** diff --git a/site/content/posts/2014-11-18-classification-systemes-stockage-nosql.md b/blog/_posts/2014-11-18-classification-systemes-stockage-nosql.md similarity index 98% rename from site/content/posts/2014-11-18-classification-systemes-stockage-nosql.md rename to blog/_posts/2014-11-18-classification-systemes-stockage-nosql.md index 5bcc5d777..6a126767e 100644 --- a/site/content/posts/2014-11-18-classification-systemes-stockage-nosql.md +++ b/blog/_posts/2014-11-18-classification-systemes-stockage-nosql.md @@ -4,7 +4,7 @@ author: Alexandre date: 2014-11-18T14:03:33+00:00 image: /img/2014-11-Sogilis-Christophe-Levet-Photographe-8877.jpg categories: - - DÉVELOPPEMENT + - Développement logiciel tags: - cassandra - couchdb @@ -15,10 +15,9 @@ tags: - nosql - redis - riak - --- -Dans un [billet précédent](https://blog.sogilis.com/posts/2014-04-01-mouvement-nosql/), nous avons établi une brève description des systèmes de stockage NoSQL (“_Not Only SQL_”) en partant des cas d'utilisation qu'ils visent à résoudre. Après avoir dégagé des points de comparaison entre ces technologies et les bases de données relationnelles, plus traditionnelles, nous en avons conclu que les moteurs de stockage NoSQL ne sont pas destinés aux mêmes usages que les bases relationnelles classiques. Par conséquent, ces deux technologies peuvent très bien cohabiter au sein d'un même logiciel sans interférer entre elles ; l'idée étant de toujours utiliser un outil adapté au problème qu'on souhaite résoudre. +Dans un [billet précédent](https://blog.sogilis.com/posts/2014-04-01-mouvement-nosql/), nous avons établi une brève description des systèmes de stockage NoSQL (“_Not Only SQL_”) en partant des cas d'utilisation qu'ils visent à résoudre. Après avoir dégagé des points de comparaison entre ces technologies et les bases de données relationnelles, plus traditionnelles, nous en avons conclu que les moteurs de stockage NoSQL ne sont pas destinés aux mêmes usages que les bases relationnelles classiques. Par conséquent, ces deux technologies peuvent très bien cohabiter au sein d'un même logiciel sans interférer entre elles ; l'idée étant de toujours utiliser un outil adapté au problème qu'on souhaite résoudre. La famille des systèmes NoSQL compte des systèmes très hétérogènes qui répondent chacun à des besoins très spécifiques. De façon générale, on arrive à les classer en quatre grands ensembles : les bases **clé-valeur**, **les bases documents**, **les bases orientées colonnes** et **les bases de type graphe**. @@ -87,6 +86,6 @@ Un résultat connu sous le nom de [lemme de Brewer _(en)_](http://en.wikipedia.o ## Références 1. Un catalogue à jour des systèmes de stockage NoSQL existants. - [nosql-database.org](http://nosql-database.org/) + [nosql-database.org](http://nosql-database.org/) 2. Une vue approfondie de plusieurs catégories de systèmes NoSQL. - [Not Only SQL _(en)_](https://en.wikipedia.org/wiki/NoSQL) + [Not Only SQL _(en)_](https://en.wikipedia.org/wiki/NoSQL) diff --git a/site/content/posts/2014-11-25-industrialisation-logiciel-implantologie-dentaire.md b/blog/_posts/2014-11-25-industrialisation-logiciel-implantologie-dentaire.md similarity index 77% rename from site/content/posts/2014-11-25-industrialisation-logiciel-implantologie-dentaire.md rename to blog/_posts/2014-11-25-industrialisation-logiciel-implantologie-dentaire.md index e4f467243..e7a040b51 100644 --- a/site/content/posts/2014-11-25-industrialisation-logiciel-implantologie-dentaire.md +++ b/blog/_posts/2014-11-25-industrialisation-logiciel-implantologie-dentaire.md @@ -4,22 +4,22 @@ author: Tiphaine date: 2014-11-25T15:01:00+00:00 image: /img/2016-04-1.Notre-vision.jpg categories: - - NOTRE VISION - - TÉMOIGNAGES CLIENTS + - Notre vision + - Témoignages clients tags: - logiciel - témoignage - video - --- + Après chaque collaboration avec un client, nous proposons de réaliser une vidéo pour dresser un bilan de la mission réalisée. Retour sur le projet de 3DKreaForm, pour qui nous avons développé la version complète d’un logiciel de planning de pose d’implants dentaires. En développant un logiciel de génération de guides d’implants dentaires pour les chirurgiens dentistes, Sogilis est intervenu sur 4 champs : -* le prototypage rapide du logiciel -* le focus fonctionnel pour une génération de revenus rapides -* la recherche de financements -* l’industrialisation de la solution logicielle +- le prototypage rapide du logiciel +- le focus fonctionnel pour une génération de revenus rapides +- la recherche de financements +- l’industrialisation de la solution logicielle Ce sont **les compétences, la rapidité et l’efficacité** durant la co-construction du logiciel qui ont marqué les fondateurs de 3DKreaForm. @@ -29,4 +29,6 @@ Les ingrédients du succès ? **Des équipes autonomes, passionnées, qui prenne **Laissons 3DKreaForm parler de son expérience avec Sogilis.** -{{< youtube N92L7xPVaDs >}} + + + diff --git a/site/content/posts/2014-12-02-notre-workflow-git-pourquoi-comment.md b/blog/_posts/2014-12-02-notre-workflow-git-pourquoi-comment.md similarity index 57% rename from site/content/posts/2014-12-02-notre-workflow-git-pourquoi-comment.md rename to blog/_posts/2014-12-02-notre-workflow-git-pourquoi-comment.md index 809d3270e..724ea91e7 100644 --- a/site/content/posts/2014-12-02-notre-workflow-git-pourquoi-comment.md +++ b/blog/_posts/2014-12-02-notre-workflow-git-pourquoi-comment.md @@ -4,12 +4,12 @@ author: Yves Brissaud date: 2014-12-02T09:03:45+00:00 image: /img/2016-04-1.Produits.jpg categories: - - DÉVELOPPEMENT + - Développement logiciel tags: - git - workflow - --- + Depuis l'avènement d'outils de gestion de code source de bonne qualité ([Git](http://git-scm.com/) et [mercurial](https://www.mercurial.fr) principalement) et surtout qui ne vous brident — presque — plus, vous pouvez enfin laisser libre court à votre imagination concernant votre flux de travail. Branches, rebase, merge…. Oui mais pour que le tout reste utilisable, surtout lorsque vous travaillez à plusieurs, il convient de régir tout ceci mais aussi de tenter de le conformer le plus possible à vos contraintes réelles — développement, production, tests…. Bref, il convient de définir un flux de travail — un _workflow_. ![](/img/2014-12-tumblr_inline_nflo1khSyE1sv6muh.png) @@ -32,7 +32,7 @@ Plutôt que de simplement vous présenter notre workflow, vous trouverez ici le Lorsque vous développez un logiciel, au début tout est facile. Ça ressemble à un historique linéaire, c'est simple, c'est clair. Et c'est facile à utiliser. Voici par exemple la séquence de commandes que vous pouvez utiliser. -{{< highlight bash >}} +```bash hack hack ... @@ -44,13 +44,13 @@ hack git add -p git commit ... -{{< /highlight >}} +``` Bon, ça c'est cool, c'est quand vous êtes tout seul. Ensuite, le problème c'est que si on ne fait pas attention ça devient tout autre chose. Vous travaillez à plusieurs et, avec les meilleurs attentions du monde, vous voulez vous mettre à jour genre, tout le temps. Et allez, c'est parti pour un florilège de `pull`, `push` parfois avec des fusions ce qui peut aussi ramener un certain nombre de conflits. -{{< highlight bash >}} +```bash hack hack ... @@ -67,7 +67,7 @@ git pull ... git commit ... -{{< /highlight >}} +``` Le problème, c'est que le résultat devient quelque peu… différent de ce qui était escompté. Pourtant, vous utilisez Git, on vous a toujours dit que les branches c'étaient bien, qu'il ne faut pas avoir peur des fusions, etc. L'un des difficultés provient du fait que, lorsqu'on utilise des [DVCS](http://en.wikipedia.org/wiki/Distributed_revision_control) à plusieurs — ou pas d'ailleurs — on crée automatiquement des branches divergentes même si il est d'usage d'avoir toujours une référence nommée et partagée (_master_ soug Git). Et forcément, s'il y a branches divergentes et qu'on utilise Git de base, il y a forcément une prolifération du nombre de fusion puisqu'on tente, naturellement, de se maintenir à jour par rapport à la base de code commune. Voici par exemple le résultat qu'on peut obtenir. @@ -111,38 +111,39 @@ Mais dans tous les cas un workflow se doit de nous aider, jamais de nous limiter Nous voulons pouvoir : -* tester facilement chaque fonctionnalité “unitairement” (vous verrez un peu plus tard que c'est un point beaucoup plus complexe qu'il n'y parait…) -* avoir un historique très lisible pour pouvoir naviguer facilement dedans lors de la découverte de mauvais comportements -* pouvoir désactiver une fonctionnalité très facilement -* avoir le détail (les étapes) de chaque fonctionnalité +- tester facilement chaque fonctionnalité “unitairement” (vous verrez un peu plus tard que c'est un point beaucoup plus complexe qu'il n'y parait…) +- avoir un historique très lisible pour pouvoir naviguer facilement dedans lors de la découverte de mauvais comportements +- pouvoir désactiver une fonctionnalité très facilement +- avoir le détail (les étapes) de chaque fonctionnalité ### Les contraintes Certains points à prendre en compte : -* pour le moment il n'y a pas de branches de production, maintenance, etc., mais ça pourra arriver un jour -* le corollaire c'est que pour le moment la branch principale doit toujours être stable -* il y a 7 (pour le moment) développeurs -* sprints de 2 semaines -* développement de logiciel mobile et de logiciel embarqué sur un drone (et là ça change tout…) +- pour le moment il n'y a pas de branches de production, maintenance, etc., mais ça pourra arriver un jour +- le corollaire c'est que pour le moment la branch principale doit toujours être stable +- il y a 7 (pour le moment) développeurs +- sprints de 2 semaines +- développement de logiciel mobile et de logiciel embarqué sur un drone (et là ça change tout…) ## Le résultat Je vais reprendre les objectifs et essayer de placer en face de chacun une “règle” Git, en prenant en compte si besoin nos contraintes. 1. **Tester unitairement chaque fonctionnalité** : - Bon là c'est simple, tout le monde me dira _« chaque fonctionnalité dans une branche dédiée »_. Oui. Mais je vous répondrai _« mais le code est dédié à un drone »_.La branche pour une fonctionnalité (_feature branch_ ou _topic branch_) c'est bien, à une condition importante, c'est qu'il existe un moyen de valider si cette branche est ok ou non avant de pouvoir l'intégrer dans le tronc commun. En général là on va parler de tests unitaires, d'intégration continue, etc. On a tout ça. Mais ça ne suffit pas. En effet, dans notre cas les tests unitaires, les tests de plus haut niveau, les simulations, l'intégration continue, tout ceci ne remplace pas — en tout cas pour le moment — des essais en vol, en extérieur. Le succès ou l'échec de notre code dépend aussi de différents matériels, de conditions extérieures — que se passe-t-il lorsque le GPS ou la communication est dégradé en plein vol parce qu'il y a des nuages ? — et pire, de ressentis visuels. Et plus que tout ceci, nous ne pouvons pas réaliser les tests en continu. Il faut se déplacer à l'extérieur pour réaliser les tests et dépendre alors de la météo. Donc nous souhaitons pouvoir tester plusieurs fonctionnalités d'un coup si c'est possible.Le résultat pour nous c'est tout de même une branche par fonctionnalité — what else? — plus une branche d'intégration spécifique à chaque itération. Lorsque nous partons en essais, la branche d'intégration comporte l'ensemble des fonctionnalités à jour ce qui permet, si tout se passe bien, de valider l'ensemble. Les fonctionnalités qui sont ok sont ensuite intégrées dans la branche principale `master`. Si jamais cela se passe mal, il est possible de générer très facilement des versions pour chaque fonctionnalité et tester, valider ou invalider chacune. + Bon là c'est simple, tout le monde me dira _« chaque fonctionnalité dans une branche dédiée »_. Oui. Mais je vous répondrai _« mais le code est dédié à un drone »_.La branche pour une fonctionnalité (_feature branch_ ou _topic branch_) c'est bien, à une condition importante, c'est qu'il existe un moyen de valider si cette branche est ok ou non avant de pouvoir l'intégrer dans le tronc commun. En général là on va parler de tests unitaires, d'intégration continue, etc. On a tout ça. Mais ça ne suffit pas. En effet, dans notre cas les tests unitaires, les tests de plus haut niveau, les simulations, l'intégration continue, tout ceci ne remplace pas — en tout cas pour le moment — des essais en vol, en extérieur. Le succès ou l'échec de notre code dépend aussi de différents matériels, de conditions extérieures — que se passe-t-il lorsque le GPS ou la communication est dégradé en plein vol parce qu'il y a des nuages ? — et pire, de ressentis visuels. Et plus que tout ceci, nous ne pouvons pas réaliser les tests en continu. Il faut se déplacer à l'extérieur pour réaliser les tests et dépendre alors de la météo. Donc nous souhaitons pouvoir tester plusieurs fonctionnalités d'un coup si c'est possible.Le résultat pour nous c'est tout de même une branche par fonctionnalité — what else? — plus une branche d'intégration spécifique à chaque itération. Lorsque nous partons en essais, la branche d'intégration comporte l'ensemble des fonctionnalités à jour ce qui permet, si tout se passe bien, de valider l'ensemble. Les fonctionnalités qui sont ok sont ensuite intégrées dans la branche principale `master`. Si jamais cela se passe mal, il est possible de générer très facilement des versions pour chaque fonctionnalité et tester, valider ou invalider chacune. 2. **Avoir un historique lisible** : - L'objectif est vraiment de pouvoir naviguer facilement dans l'historique, essentiellement pour y rechercher la cause d'un mauvais comportement qui n'aurait pas été mis en évidence par les tests automatisés.La première solution à mettre en place c'est de limiter au maximum les commits “sans valeur”, par exemple les commits de synchronisation avec l’_upstream_, et garantir le meilleur rapport signal/bruit possible. Pour ça c'est assez facile, il suffit d'interdir les `pull/merge` de synchronisation. Si on souhaite tout de même bénéficier d'améliorations qui sont dans le tronc commun, il faut utiliser `rebase` ce qui linéarise l'historique.La deuxième chose c'est d'éviter au maximum les croisements de branches. La solution passe également par l'utilisation systématique de `rebase` avant d'intégrer les changements.Ceci doit permettre de ne pas avoir de commits inutiles et donc de pouvoir lire facilement l'historique car plus linéaire, moins plat de spaghettis. + L'objectif est vraiment de pouvoir naviguer facilement dans l'historique, essentiellement pour y rechercher la cause d'un mauvais comportement qui n'aurait pas été mis en évidence par les tests automatisés.La première solution à mettre en place c'est de limiter au maximum les commits “sans valeur”, par exemple les commits de synchronisation avec l’_upstream_, et garantir le meilleur rapport signal/bruit possible. Pour ça c'est assez facile, il suffit d'interdir les `pull/merge` de synchronisation. Si on souhaite tout de même bénéficier d'améliorations qui sont dans le tronc commun, il faut utiliser `rebase` ce qui linéarise l'historique.La deuxième chose c'est d'éviter au maximum les croisements de branches. La solution passe également par l'utilisation systématique de `rebase` avant d'intégrer les changements.Ceci doit permettre de ne pas avoir de commits inutiles et donc de pouvoir lire facilement l'historique car plus linéaire, moins plat de spaghettis. 3. **Pouvoir désactiver une fonctionnalité** : - Le scénario est le suivant : on détecte après coup une fonctionnalité qui pose problème (ou simplement on veut supprimer une fonctionnalité).Il faut alors pouvoir visualiser très rapidement la fonctionnalité et l'ensemble de ses modifications. La pire chose qui existerait c'est de faire du merge en _fast forward_, c'est-à-dire une linéarisation des commits de la branche. On les rajoute simplement au-dessus du tronc commun. Si on fait ça — et ceux qui ont fait du `svn` (pouah !) connaissent très bien — il devient très compliqué d'identifier l'ensemble des modifications liées à une fonctionnalité. Et donc il devient très compliqué de les annuler.La solution est donc d'avoir tant que possible un unique commit pour chaque intégration de fonctionnalité dans le tronc commun. Si cela est fait, on peut annuler facilement par la réalisation d'un commit inversé. Vous pouvez utiliser directement la commande [`git revert`](https://www.atlassian.com/git/tutorials/undoing-changes/git-revert/) pour le faire. A ce moment de décision, vous avez deux choix : - * faire des merges systématiquement sans _fast forward_ : `git merge --no-ff` - * faire des merges avec fusion de tous les commits en un seul : `git merge --squash` + Le scénario est le suivant : on détecte après coup une fonctionnalité qui pose problème (ou simplement on veut supprimer une fonctionnalité).Il faut alors pouvoir visualiser très rapidement la fonctionnalité et l'ensemble de ses modifications. La pire chose qui existerait c'est de faire du merge en _fast forward_, c'est-à-dire une linéarisation des commits de la branche. On les rajoute simplement au-dessus du tronc commun. Si on fait ça — et ceux qui ont fait du `svn` (pouah !) connaissent très bien — il devient très compliqué d'identifier l'ensemble des modifications liées à une fonctionnalité. Et donc il devient très compliqué de les annuler.La solution est donc d'avoir tant que possible un unique commit pour chaque intégration de fonctionnalité dans le tronc commun. Si cela est fait, on peut annuler facilement par la réalisation d'un commit inversé. Vous pouvez utiliser directement la commande [`git revert`](https://www.atlassian.com/git/tutorials/undoing-changes/git-revert/) pour le faire. A ce moment de décision, vous avez deux choix : + +- faire des merges systématiquement sans _fast forward_ : `git merge --no-ff` +- faire des merges avec fusion de tous les commits en un seul : `git merge --squash` 4. **Avoir le détail de chaque fonctionnalité** : - Pour pouvoir débugger plus facilement mais aussi simplement relire et comprendre les modifications, il est intéressant de garder présentes les étapes de développement. + Pour pouvoir débugger plus facilement mais aussi simplement relire et comprendre les modifications, il est intéressant de garder présentes les étapes de développement. Ceci interdit donc l'utilisation de `merge --squash` au profit de `merge --no-ff`. En effet, dans ce cas nous avons un commit de merge mais la branche et donc le détail des opérations restent visibles. @@ -152,12 +153,12 @@ Le rebase va obliger à réécrire l'historique et donc probablement à forcer l ### En résumé -* une branche par fonctionnalité -* une branche d'intégration par itération -* synchronisation uniquement par _rebase_ -* _rebase_ obligatoire avant intégration -* fusion sans _fast forward_ obligatoire -* nettoyage des branches avec du _rebase_ interactif +- une branche par fonctionnalité +- une branche d'intégration par itération +- synchronisation uniquement par _rebase_ +- _rebase_ obligatoire avant intégration +- fusion sans _fast forward_ obligatoire +- nettoyage des branches avec du _rebase_ interactif ## La mise en œuvre @@ -167,99 +168,108 @@ Vous vous souvenez de l'historique horrible du début de l'article ? Maintenant Ceci est une capture du vrai résultat, sur le même projet. Bon ok vous n'avez pas les commentaires des commits, mais voici ce qu'on peut en tirer : -* l'historique est clair et lisible, il est tout à fait possible de le comprendre et de se déplacer dedans sans craintes -* l'historique nous offre deux niveaux de détails : - * l'intégration de chaque fonctionnalité / bug / … - * le détail de chaque fonctionnalité / bug / … -* étant donné qu'il est facile d'identifier le commit d'intégration d'une fonctionnalité, il est aussi facile de l'annuler -* on voit que chaque fonctionnalité a été réalisée dans une branche dédiée, ce qui permet de la tester unitairement -* comme chaque branche a subit un `rebase` avant d'être fusionnée, il n'y a pas de croisements de branches, ce qui améliore la lisibilité -* une branche d'intégration par itération est créée, puis si tout est ok, fusionnée dans `master`. Vous le voyez avec `origin/integ` qui a été fusionné (en _fast forward_, c'est le seul cas) dans `origin/master` et tout ce qui est au-dessus est dans `origin/integ-it2.7` et non dans `master` car l'itération est en cours. Evidemment il pourrait y avoir des branches non fusionnées dans `integ-it2.7`. +- l'historique est clair et lisible, il est tout à fait possible de le comprendre et de se déplacer dedans sans craintes +- l'historique nous offre deux niveaux de détails : + - l'intégration de chaque fonctionnalité / bug / … + - le détail de chaque fonctionnalité / bug / … +- étant donné qu'il est facile d'identifier le commit d'intégration d'une fonctionnalité, il est aussi facile de l'annuler +- on voit que chaque fonctionnalité a été réalisée dans une branche dédiée, ce qui permet de la tester unitairement +- comme chaque branche a subit un `rebase` avant d'être fusionnée, il n'y a pas de croisements de branches, ce qui améliore la lisibilité +- une branche d'intégration par itération est créée, puis si tout est ok, fusionnée dans `master`. Vous le voyez avec `origin/integ` qui a été fusionné (en _fast forward_, c'est le seul cas) dans `origin/master` et tout ce qui est au-dessus est dans `origin/integ-it2.7` et non dans `master` car l'itération est en cours. Evidemment il pourrait y avoir des branches non fusionnées dans `integ-it2.7`. ### Et en pratique ? Voici les quelques commandes / principes que nous utilisons pour mettre en œuvre ce workflow. 1. On suppose qu'on débute une itération, `master` est propre. Par défaut toutes les branches vont avoir initialement comme origine `master`. - {{< highlight bash >}} - git checkout master - git checkout -b integ - {{< /highlight >}} + +```bash +git checkout master +git checkout -b integ +``` 2. Pour une fonctionnalité donnée, on crée une branche pour bosser dedans. - {{< highlight bash >}} - git checkout master - git checkout -b feature/my-super-cool-feature - {{< /highlight >}} - Nos branches sont préfixées pour améliorer la lisibilité : - * `feature/` pour les fonctionnalités - * `bug/` pour les anomalies - * `refactor/` pour ce qui est lié à du pur refactoring (on en a beaucoup puisqu'on se base sur un existant pas toujours propre…) +```bash +git checkout master +git checkout -b feature/my-super-cool-feature +``` + +Nos branches sont préfixées pour améliorer la lisibilité : + +- `feature/` pour les fonctionnalités +- `bug/` pour les anomalies +- `refactor/` pour ce qui est lié à du pur refactoring (on en a beaucoup puisqu'on se base sur un existant pas toujours propre…) 3. On développe dans la branche. - {{< highlight bash >}} - git checkout feature/my-super-cool-feature - ... - hack - ... - git add -p - git commit - ... - hack - ... - git add -p - git commit - ... - git push # avec -u pour configurer l'upstream la première fois - ... - {{< /highlight >}} + +```bash +git checkout feature/my-super-cool-feature +... +hack +... +git add -p +git commit +... +hack +... +git add -p +git commit +... +git push # avec -u pour configurer l'upstream la première fois +... +``` 4. S'il est nécessaire de se synchroniser, on rebase. Attention, j'insiste sur le nécessaire, si ce n'est pas obligatoire on ne le fait pas maintenant. - {{< highlight bash >}} - git checkout integ # je suppose que je suis dans ma branche de fonctionnalité - git pull # comme on ne développe jamais dans master et qu'on fast forward master - # on peut laisser le pull sans rebase - git checkout - - git rebase - - {{< /highlight >}} - A ce moment, pour pousser mes modifications sur le serveur il faut que j'écrase la branche distante. Comme ce n'est qu'une branche de fonctionnalité et qu'il n'y a pas plusieurs personnes — hors binome — qui travaille dessus, c'est permis. +```bash +git checkout integ # je suppose que je suis dans ma branche de fonctionnalité +git pull # comme on ne développe jamais dans master et qu'on fast forward master + # on peut laisser le pull sans rebase +git checkout - +git rebase - +``` + +A ce moment, pour pousser mes modifications sur le serveur il faut que j'écrase la branche distante. Comme ce n'est qu'une branche de fonctionnalité et qu'il n'y a pas plusieurs personnes — hors binome — qui travaille dessus, c'est permis. - {{< highlight bash >}} - git push -f - {{< /highlight >}} +```bash +git push -f +``` 5. Fusion dans la branche d'intégration. Pour commencer on se synchronise et rebase avec, cf `4.`Ensuite on s'occupe de nettoyer la branche, avec `rebase -i` voir `rebase -i --autosquash` si vous avez pensé à l'utiliser. Enfin, on fusionne sans faire de fast forward. - {{< highlight bash >}} - git checkout integ - git pull - git checkout - - git rebase - - git merge --no-ff integ - {{< /highlight >}} +```bash +git checkout integ +git pull +git checkout - +git rebase - + +git merge --no-ff integ +``` - Concernant le message de commit il y a deux choix. Soit le nom de la branche est déjà explicite et ok, soit on met un beau message bien propre qui indique la fonctionnalité qu'on vient d'intégrer (à préférer). +Concernant le message de commit il y a deux choix. Soit le nom de la branche est déjà explicite et ok, soit on met un beau message bien propre qui indique la fonctionnalité qu'on vient d'intégrer (à préférer). - Evidemment on pousse la branche d'intégration sur le serveur. +Evidemment on pousse la branche d'intégration sur le serveur. 6. Si les tests automatiques et non ont montré que la fonctionnalité ainsi que la branche integ sont ok, on peut fusionner integ dans master. - {{< highlight bash >}} - git checkout master - git merge --ff - git push - {{< /highlight >}} - Etant donné qu'on vient de faire une fusion _fast forward_ de `integ`, il n'est pas nécessaire de faire un `rebase` ou autre de cette dernière. +```bash +git checkout master +git merge --ff +git push +``` + +Etant donné qu'on vient de faire une fusion _fast forward_ de `integ`, il n'est pas nécessaire de faire un `rebase` ou autre de cette dernière. 7. On nettoie un peu nos branches, c'est-à-dire qu'on ne garde pas sur le serveur de branches fusionnées et terminées, histoire de garder un ensemble lisible. - {{< highlight bash >}} - git branch -d feature/my-super-cool-feature - git push origin --delete feature/my-super-cool-feature - {{< /highlight >}} + +```bash +git branch -d feature/my-super-cool-feature +git push origin --delete feature/my-super-cool-feature +``` ## À suivre + ======= Aujourd'hui le workflow tel que défini est une aide précieuse dans notre développement. Il reste des points toujours délicats autour de la branche d'intégration. L'idéal serait de pouvoir valider nos modifications plus facilement, et donc de fusionner directement dans `master` et ne plus avoir cette branche intermédiaire. Mais cela est directement lié au métier et non une simple contrainte d'outillage. @@ -274,22 +284,22 @@ Si vous souhaitez aller plus loin, ou juste apprendre Git, nous [donnons des for Et si vous n'êtes pas rassasiés, voici une petite collection de liens à suivre : -* Git Flow - * [A successful Git branching model](http://nvie.com/posts/a-successful-git-branching-model/) - * [GitFlow](https://github.com/nvie/gitflow) - * [GitFlow by Atlassian](https://www.atlassian.com/git/tutorials/comparing-workflows/gitflow-workflow) -* [Github flow](https://guides.github.com/introduction/flow/index.html) -* [Git revert](https://www.atlassian.com/git/tutorials/undoing-changes/git-revert/) -* Git rebase - * [chez Atlassian](https://www.atlassian.com/git/tutorials/rewriting-history/git-rebase) - * [doc Git](http://git-scm.com/book/fr/Les-branches-avec-Git-Rebaser) -* Git rebase -i - * [chez Atlassian](https://www.atlassian.com/git/tutorials/rewriting-history/git-rebase-i) - * [doc Git](http://git-scm.com/book/en/Git-Tools-Rewriting-History) -* Git rebase -i –autosquash - * [protip Coderwall](https://coderwall.com/p/hh-4ea) - * [keep your branch clean with fixup and autosquash](http://fle.github.io/git-tip-keep-your-branch-clean-with-fixup-and-autosquash.html) -* Git rerere - * [doc Git](http://git-scm.com/blog/2010/03/08/rerere.html) - * [Git rerere ma commande préférée](http://hypedrivendev.wordpress.com/2013/08/30/git-rerere-ma-commande-preferee/) -* Si vous voulez comprendre pourquoi et comment utiliser rebase et avoir un historique propre, je vous conseille vivement [ce post de Linus Torvalds sur la lkml](http://www.mail-archive.com/dri-devel@lists.sourceforge.net/msg39091.html). C'est plein de bons conseils pour bien utiliser Git. +- Git Flow + - [A successful Git branching model](http://nvie.com/posts/a-successful-git-branching-model/) + - [GitFlow](https://github.com/nvie/gitflow) + - [GitFlow by Atlassian](https://www.atlassian.com/git/tutorials/comparing-workflows/gitflow-workflow) +- [Github flow](https://guides.github.com/introduction/flow/index.html) +- [Git revert](https://www.atlassian.com/git/tutorials/undoing-changes/git-revert/) +- Git rebase + - [chez Atlassian](https://www.atlassian.com/git/tutorials/rewriting-history/git-rebase) + - [doc Git](http://git-scm.com/book/fr/Les-branches-avec-Git-Rebaser) +- Git rebase -i + - [chez Atlassian](https://www.atlassian.com/git/tutorials/rewriting-history/git-rebase-i) + - [doc Git](http://git-scm.com/book/en/Git-Tools-Rewriting-History) +- Git rebase -i –autosquash + - [protip Coderwall](https://coderwall.com/p/hh-4ea) + - [keep your branch clean with fixup and autosquash](http://fle.github.io/git-tip-keep-your-branch-clean-with-fixup-and-autosquash.html) +- Git rerere + - [doc Git](http://git-scm.com/blog/2010/03/08/rerere.html) + - [Git rerere ma commande préférée](http://hypedrivendev.wordpress.com/2013/08/30/git-rerere-ma-commande-preferee/) +- Si vous voulez comprendre pourquoi et comment utiliser rebase et avoir un historique propre, je vous conseille vivement [ce post de Linus Torvalds sur la lkml](http://www.mail-archive.com/dri-devel@lists.sourceforge.net/msg39091.html). C'est plein de bons conseils pour bien utiliser Git. diff --git a/site/content/posts/2014-12-09-git-windows-unix-problemes-eol.md b/blog/_posts/2014-12-09-git-windows-unix-problemes-eol.md similarity index 90% rename from site/content/posts/2014-12-09-git-windows-unix-problemes-eol.md rename to blog/_posts/2014-12-09-git-windows-unix-problemes-eol.md index 0133b073f..e14558848 100644 --- a/site/content/posts/2014-12-09-git-windows-unix-problemes-eol.md +++ b/blog/_posts/2014-12-09-git-windows-unix-problemes-eol.md @@ -4,7 +4,7 @@ author: Tiphaine date: 2014-12-09T11:18:00+00:00 image: /img/2016-04-2.Developpement-1.jpg categories: - - DÉVELOPPEMENT + - Développement logiciel tags: - EndOfLine - EOL @@ -12,31 +12,31 @@ tags: - gitattributes - Linux - Windows - --- + Récemment, nous avions à créer un environnement de développement pour un projet web. Le client développe sous Windows. Préférant Unix dans l'équipe, nous avons commencé à travailler sur nos machines. Rapidement, nous avons eu un squelette d'application fonctionnelle dont les sources étaient versionnées sous Git. On utilisait `Vagrant` pour faire tourner l'application avec une debian afin de coller à la configuration du serveur de production. Et puis, les install sur une machine Windows, ce n'est pas ma tasse de thé. Après avoir testé et retesté les scripts de déploiement de `Vagrant` sur nos machines, nous avons lancé `vagrant up` sur la machine Windows suivi de `vagrant provision`. - + Ce dernier, appelle un script shell, permettant de déployer la base de données MySQL associée au projet. Et là, horreur, les scripts shells n'arrivent pas à se lancer. L'erreur est même bizarre puisque : -{{< highlight bash >}} +```bash ./scripts/mon_script.sh could not be found -{{< /highlight >}} +``` Je fais donc un `vagrant ssh` pour me connecter à la machine. Et un simple `vim scripts/mon_script.sh` me donne un code : -{{< highlight bash >}} +```bash # /bin/sh^M // init ...^M echo "blabla"^M -{{< /highlight >}} +``` Bref, le constat est clair : il y a des fins de lignes qui ne sont pas compatibles avec Unix et donc il produit une erreur. @@ -68,14 +68,14 @@ Le LF est utilisé sur les systèmes Unix/Linux et est représenté par `n` Pour cela on a plusieurs possibilités : -* utiliser la config `core.autocrlf` de Git avec éventuellement une option `--global` pour l'avoir sur tous ses repos. Mais la config n'est pas partagée et un nouvel utilisateur devra la spécifier au moment de démarrer sur le projet : pas idéal. -* utiliser les attributes de Git, c'est-à-dire créer un fichier `.gitattributes` à la racine du départ et y ajouter la ligne suivante `* text=auto eol=lf`. Ajouter le fichier .gitattributes en le committant ou en l'ajoutant à la staging area. +- utiliser la config `core.autocrlf` de Git avec éventuellement une option `--global` pour l'avoir sur tous ses repos. Mais la config n'est pas partagée et un nouvel utilisateur devra la spécifier au moment de démarrer sur le projet : pas idéal. +- utiliser les attributes de Git, c'est-à-dire créer un fichier `.gitattributes` à la racine du départ et y ajouter la ligne suivante `* text=auto eol=lf`. Ajouter le fichier .gitattributes en le committant ou en l'ajoutant à la staging area. ### Réinitialiser le repo Pour cela, il suffit de : -{{< highlight bash >}} +```bash git add . -u git commit -m "Sauvegarder vos modifications en cours" # supprimer tous les fichiers versionnés dans git @@ -87,12 +87,12 @@ git checkout -- . # Enregister le changement de ligne git add . $ git commit -m "Normalize all the line endings" -{{< /highlight >}} +``` Et voilà, maintenant une personne qui clone le repo sera immédiatement dans la bonne configuration. ## Quelques liens intéressants -* [git config](http://git-scm.com/docs/git-config) -* [vagrant](https://www.vagrantup.com/) -* [EOF github](https://help.github.com/articles/dealing-with-line-endings/) +- [git config](http://git-scm.com/docs/git-config) +- [vagrant](https://www.vagrantup.com/) +- [EOF github](https://help.github.com/articles/dealing-with-line-endings/) diff --git a/site/content/posts/2014-12-24-joyeux-noel.md b/blog/_posts/2014-12-24-joyeux-noel.md similarity index 96% rename from site/content/posts/2014-12-24-joyeux-noel.md rename to blog/_posts/2014-12-24-joyeux-noel.md index b0016635f..00cb02af5 100644 --- a/site/content/posts/2014-12-24-joyeux-noel.md +++ b/blog/_posts/2014-12-24-joyeux-noel.md @@ -4,18 +4,18 @@ author: Tiphaine date: 2014-12-24T14:13:36+00:00 image: /img/2016-04-5.png categories: - - LA VIE À SOGILIS + - La vie à sogilis tags: - Fun - Noël - --- + **Noël au bureau, c'est comment ?** C'est comme ça : ![](https://67.media.tumblr.com/7ba6af622290aa2bae36d2eb65a68171/tumblr_inline_ngqcggZQzE1t2p7ex.jpg) - + Comme ça aussi : ![](https://67.media.tumblr.com/0b7f60da19fb92bca3fc35d9608312d8/tumblr_inline_ngqc47zpHk1t2p7ex.jpg) diff --git a/site/content/posts/2014-12-30-temoignage-equipe.md b/blog/_posts/2014-12-30-temoignage-equipe.md similarity index 98% rename from site/content/posts/2014-12-30-temoignage-equipe.md rename to blog/_posts/2014-12-30-temoignage-equipe.md index 67a313907..25c3e05be 100644 --- a/site/content/posts/2014-12-30-temoignage-equipe.md +++ b/blog/_posts/2014-12-30-temoignage-equipe.md @@ -4,12 +4,12 @@ author: Tiphaine date: 2014-12-30T13:37:25+00:00 image: /img/2016-04-1.Design.jpg categories: - - NOTRE VISION + - Notre vision tags: - Sogiteam - témoignage - --- + Quelques nouvelles : notre entreprise grenobloise s’agrandit. Nous sommes aujourd’hui une vingtaine et nous nous installerons bientôt dans de nouveaux locaux à Europole. Depuis octobre, nous étendons notre présence en Rhône-Alpes : [nous accélérons les start-up à Lyon](http://www.ledauphine.com/isere-sud/2014/11/04/sogilis-accelere-les-start-up-a-lyon-gldb). Deux membres de la Sogiteam ont récemment été interviewés sur leur travail au quotidien. On dit souvent que Sogilis est une entreprise libérée. Mais concrètement, qu'est-ce que cela signifie ? diff --git a/site/content/posts/2015-01-15-vision-business.md b/blog/_posts/2015-01-15-vision-business.md similarity index 96% rename from site/content/posts/2015-01-15-vision-business.md rename to blog/_posts/2015-01-15-vision-business.md index c7d455053..0b4d1f786 100644 --- a/site/content/posts/2015-01-15-vision-business.md +++ b/blog/_posts/2015-01-15-vision-business.md @@ -4,17 +4,17 @@ author: Xavier Spengler date: 2015-01-15T13:10:00+00:00 image: /img/2016-04-3.CS_BUSINESS_BasPage.jpg categories: - - NOTRE VISION + - Notre vision tags: - accompagnement - businessmodel - --- + ## Money, Money, Money Comme le disait Clint Eastwood : _« Tu vois, le monde se divise en deux catégories : ceux qui ont un pistolet chargé et ceux qui creusent. Toi, tu creuses. »_ -Pour les start-up, c’est globalement du pareil au même et elles se définissent facilement de la manière suivante. On pourrait dire : _« Tu vois, les start-up se divisent en deux catégories : celles qui ont de l’argent et celles qui creusent. Toi, tu creuses. »_ +Pour les start-up, c’est globalement du pareil au même et elles se définissent facilement de la manière suivante. On pourrait dire : *« Tu vois, les start-up se divisent en deux catégories : celles qui ont de l’argent et celles qui creusent. Toi, tu creuses. »* Aujourd’hui, quand je rencontre des porteurs de projets, ce qu’ils souhaitent en premier lieu, c’est de l’argent, beaucoup d’argent : celui de l’état, des investisseurs, des Business Angels, et aujourd’hui celui des Kickstarter et consors. Tout cela avant même d’avoir vendu le moindre produit ou le moindre service, et surtout d’avoir rencontré un futur client, s’être confronté au marché. Vous l’aurez compris : sans argent pas de projet, sans projet pas d’argent, tout cela se mord facilement la queue au risque de voir son business plan _« émasculé »_ avant même d’avoir engrangé le premier €uro ! Souvent, demander de l’argent, des aides financières trop tôt n’est qu’un moyen pour repousser le moment où l’on se prend le mur… @@ -33,7 +33,7 @@ Le premier jet de votre canvas ne vous servira pas plus que votre tableur si vo L’élaboration de ces canvas doit impérativement se réaliser en mode itératif, avec un processus constructif et évolutif. C’est ainsi que vous allez entrer dans un système bannissant les plans irréalistes, les idées préconçues, et ainsi faire évoluer votre partition. -**Conseil n°2 : Faites vous aider. Disposer d’un regard externe peut vous faire gagner du temps, encore faut-il qu’il soit critique et puisse vous faire pivoter si besoin. Si possible, excluez les _« bons amis »_ et les _« meilleurs potes de 20 ans »_ qui auront globalement des avis tranchés, dans un sens comme dans l’autre.** +**Conseil n°2 : Faites vous aider. Disposer d’un regard externe peut vous faire gagner du temps, encore faut-il qu’il soit critique et puisse vous faire pivoter si besoin. Si possible, excluez les _« bons amis »_ et les *« meilleurs potes de 20 ans »* qui auront globalement des avis tranchés, dans un sens comme dans l’autre.** ## Catégoriser votre business, vous dormirez mieux @@ -57,14 +57,14 @@ Le client est une sorte de lame en céramique qui serait capable d’anéantir - Il est indécis, c’est qu’il n’est pas encore au courant que vous détenez la dernière meilleure idée du siècle. - Il ne veut pas de votre produit, c’est qu’il n’a rien compris à ce que vous lui proposez. - Il doute de l’utilité de votre produit, c’est un _« français »_, il doute de tout. -- Il n’est pas là, au bon moment, au bon endroit, quel _« con » _ce client. Le pire, c’est qu’il critique votre produit ! Insupportable, inadmissible. +- Il n’est pas là, au bon moment, au bon endroit, quel \_« con » _ce client. Le pire, c’est qu’il critique votre produit ! Insupportable, inadmissible. - Sauf que : le client a toujours raison, ou presque, d’autant plus que c’est lui qui va acheter votre produit. La principale conséquence des idées préconçues ci-dessus est que vous n’allez pas le rencontrer. Vous allez penser à sa place, c’est quand même plus simple, plus confortable : il fait peur. **Conseil n°4 : n’oubliez pas que vous n’êtes pas forcément le client de ce que vous produisez. Se confronter au marché n’est pas une chose simple. Il existe pourtant des techniques éprouvées à mettre en oeuvre pour casser cette barrière. Elles doivent être réalisées au cas par cas en fonction des hypothèses marché que l’on peut émettre.** ## Des conseils pour mon business, non, pas pour moi -Se faire accompagner a un coût, je ne le cache pas, et beaucoup de porteurs de projets économisent de manière naturelle sur ce poste de dépenses. J’ai forcément un avis biaisé, j’en conviens. Cet accompagnement a d’autant plus un coût que la plupart des startupers sont persuadés d’avoir en leurs mains l’idée du siècle, qu’ils vont mener à terme sans =embûche. Au passage, ils en profiteront pour tenir secret leur bébé, jusqu’à terme – si terme il y a –, en évitant le syndrome de la _« fausse couche »_. +Se faire accompagner a un coût, je ne le cache pas, et beaucoup de porteurs de projets économisent de manière naturelle sur ce poste de dépenses. J’ai forcément un avis biaisé, j’en conviens. Cet accompagnement a d’autant plus un coût que la plupart des startupers sont persuadés d’avoir en leurs mains l’idée du siècle, qu’ils vont mener à terme sans =embûche. Au passage, ils en profiteront pour tenir secret leur bébé, jusqu’à terme – si terme il y a –, en évitant le syndrome de la *« fausse couche »*. Dans ce contexte, ils vont tout faire pour économiser sur des postes qu’ils vont juger opportuns. Rien de plus normal : chacun connaît ses postes de dépenses et les coupes franches vont bon train, ces dernières étant logiquement arbitrées par le bon sens, les impressions et le feeling – au choix –, quitte à repousser le moment où l’on va se faire accompagner. @@ -80,7 +80,6 @@ La partie business est la pierre angulaire de notre accompagnement, elle va dét **Conseil n°5 : Se faire accompagner n’est pas un signe de faiblesse. Se faire réellement accompagner – cf. l’analogie au sucre glace – ne doit pas être considéré comme un luxe, destiné à ceux auront trouvé la solution de qui de l’oeuf ou de la poule est arrivé le premier. Votre temps est précieux, et malheureusement votre temps c’est de l’argent : Money, Money, Money… CQFD.** - **_A propos de l’auteur_** _**Xavier Spengler** développe l’activité lyonnaise de Sogilis, il est spécialisé dans l'accompagnement business. Il accompagne les porteurs de projets (numérique, électronique, web…), leur permettant d’accéder le plus rapidement possible aux premiers revenus et rendre leurs projets pérennes._ diff --git a/site/content/posts/2015-02-03-decouverte-amorcage-projet-storymap.md b/blog/_posts/2015-02-03-decouverte-amorcage-projet-storymap.md similarity index 88% rename from site/content/posts/2015-02-03-decouverte-amorcage-projet-storymap.md rename to blog/_posts/2015-02-03-decouverte-amorcage-projet-storymap.md index 0d3c77ca1..4961903bc 100644 --- a/site/content/posts/2015-02-03-decouverte-amorcage-projet-storymap.md +++ b/blog/_posts/2015-02-03-decouverte-amorcage-projet-storymap.md @@ -1,10 +1,10 @@ --- -title: "Découverte et amorçage de projet : le problème avec les story maps" +title: 'Découverte et amorçage de projet : le problème avec les story maps' author: Simon Denier date: 2015-02-03T14:32:00+00:00 image: /img/2015-02-Jérémy2.jpg categories: - - DÉVELOPPEMENT + - Développement logiciel tags: - agile - capabilities @@ -12,8 +12,8 @@ tags: - project - risk - storymap - --- + Tout projet commence par une phase d'élaboration et de maturation des idées, que ce soit sur la problématique et les objectifs, le financement, la commercialisation, et bien sûr la technique. L'équipe technique se retrouve alors confrontée à la question de la réalisation : que doit-on faire, quels sont les délais, combien ça va coûter ? Un des outils mis en avant pour planifier un projet en mode agile est la story map. Or notre expérience avec cet outil s'est révélée très hétérogène voire paradoxale. En effet, elle n'est pas forcément pertinente pour répondre aux questions essentielles en tout début de projet : combien investir et quelle est la prochaine étape ? Ce premier article fouille quelques-uns des défauts constatés avec l'approche story map, avant d'aborder la prochaine fois une autre approche basée sur les capacités et les risques. ## Petit rappel : ce qu'est une story map @@ -38,11 +38,11 @@ Ces porteurs arrivent avec des maturités différentes vis-à-vis de leur projet Nous avions donc besoin **d'une méthode pour nous aider à découvrir rapidement un projet et à encadrer les clients lors des phases de découverte.** La story map semblait un outil intéressant pour cela. De plus utiliser cet outil nous donnaient une chance d'être actif dans la recherche d'informations, a contrario de la lecture d'un cahier des charges, plus passive et fastidieuse. Cela nous permettait de dénicher plus facilement des points obscurs - interpeller le porteur de projets et le faire sortir de ses habitudes - et d'exposer de futurs clients à nos méthodes : -* atelier favorisant la discussion entre participants -* post-its pour garder les idées concises et essentielles -* focus sur les utilisateurs -* priorisation et construction des lots dynamiques, en groupe… -* vision complète et amélioration incrémentale pour favoriser le raffinement des points clés +- atelier favorisant la discussion entre participants +- post-its pour garder les idées concises et essentielles +- focus sur les utilisateurs +- priorisation et construction des lots dynamiques, en groupe… +- vision complète et amélioration incrémentale pour favoriser le raffinement des points clés Sur le papier, la story map avait donc tout pour plaire. **Mais notre expérience avec les story maps en découverte de projet s'est révélée très hétérogène voire paradoxale.** Parfois cela marchait bien, révélant les différents aspects du projet. Régulièrement, le résultat ne semblait pas à la hauteur des efforts fournis lors de la session. Dans au moins deux cas, la story map s'est révélée complètement inadaptée. Pourtant, c'est une étape structurante dans la vie d'un projet, d'autant plus qu'il s'agit souvent du premier contact entre l'équipe technique et le porteur de projet. Il est donc frustrant de commencer par une session de travail qui ne marche pas bien. @@ -52,8 +52,8 @@ En bons agilistes, nous avons essayé d'adapter l'outil et ses règles pour rend Malgré ces adaptations au fil de l'eau, les story maps se finissaient souvent pour nous sur les constats suivant : -* c'est usant, voire stressant, et malgré nos efforts nous n'étions pas sûr d'avoir une vision correcte du projet ! -* d'une story map à l'autre, le niveau d'expression et d'abstraction était très différent et ne permettait pas la même exploitation des résultats : tel aspect évoqué par les participants n'apparaissait pas clairement dans la story map, tel autre manquait de précision et entraînait des incompréhensions et des décisions mal ajustées… +- c'est usant, voire stressant, et malgré nos efforts nous n'étions pas sûr d'avoir une vision correcte du projet ! +- d'une story map à l'autre, le niveau d'expression et d'abstraction était très différent et ne permettait pas la même exploitation des résultats : tel aspect évoqué par les participants n'apparaissait pas clairement dans la story map, tel autre manquait de précision et entraînait des incompréhensions et des décisions mal ajustées… ## Les problèmes des story maps en découverte de projet @@ -101,15 +101,15 @@ Malgré ces inconvénients, nous croyons quand même dans la valeur de la story _Equipe technique_ -* Nous avons une première vue de fonctionnalités minimales, nous pouvons commencer au plus vite pour fournir un produit minimum viable. -* Quand voulez-vous commencer ? +- Nous avons une première vue de fonctionnalités minimales, nous pouvons commencer au plus vite pour fournir un produit minimum viable. +- Quand voulez-vous commencer ? _Porteur de projet_ -* Est-ce que la story map correspond à la liste des fonctionnalités ? Qu'est-ce qui va rentrer dans le périmètre final ? -* Combien ça va coûter ? +- Est-ce que la story map correspond à la liste des fonctionnalités ? Qu'est-ce qui va rentrer dans le périmètre final ? +- Combien ça va coûter ? -Bizarrement, cette dernière question provoquait toujours chez nous une sensation de malaise. Une story map ne permet pas de fournir une meilleure estimation qu'une autre méthode (tout au plus favorise-t-elle la comparaison pour un chiffrage relatif *). Il nous manquait toujours des informations cruciales au moment du chiffrage pour être serein. **Au final, nous nous sentions piégés dans les vieilles habitudes de chiffrage du cahier des charges.** De plus, la story map ne nous offrait pas toujours une vision claire sur la prochaine étape : peut-on commencer tout de suite ? Y a-t-il d'autres étapes non techniques à valider d'abord ? +Bizarrement, cette dernière question provoquait toujours chez nous une sensation de malaise. Une story map ne permet pas de fournir une meilleure estimation qu'une autre méthode (tout au plus favorise-t-elle la comparaison pour un chiffrage relatif \*). Il nous manquait toujours des informations cruciales au moment du chiffrage pour être serein. **Au final, nous nous sentions piégés dans les vieilles habitudes de chiffrage du cahier des charges.** De plus, la story map ne nous offrait pas toujours une vision claire sur la prochaine étape : peut-on commencer tout de suite ? Y a-t-il d'autres étapes non techniques à valider d'abord ? ## La story map est-elle utile ? @@ -117,5 +117,4 @@ On dit « si tu as un marteau dans la tête, alors toutes les vis ressemblent à Bien sûr, il ne s'agit pas de remettre en cause la story map comme outil de planification de projet, quand celui-ci devient plus mature, que le périmètre des fonctionnalités se dessine et que les priorités sont identifiées. Simplement, elle ne peut pas venir trop tôt dans la découverte d'un projet, car **elle risque de figer des décisions sur les fonctionnalités à un moment où celles-ci sont encore méconnues.** Nous verrons dans le prochain article une approche qui se focalise sur les capacités et les risques du projet, afin de trouver une organisation plus pragmatique du développement. - -_* A propos des estimations, comment les faire, les exploiter intelligemment et les éviter quand ce n'est pas utile, je recommande Dan North ([The Perils of Estimation](http://dannorth.net/2009/07/01/the-perils-of-estimation/), [Blink Estimation](http://dannorth.net/2013/08/08/blink-estimation/)) et aussi tout ce qui touche au mouvement NoEstimates. Mais c'est un autre sujet._ +_\* A propos des estimations, comment les faire, les exploiter intelligemment et les éviter quand ce n'est pas utile, je recommande Dan North ([The Perils of Estimation](http://dannorth.net/2009/07/01/the-perils-of-estimation/), [Blink Estimation](http://dannorth.net/2013/08/08/blink-estimation/)) et aussi tout ce qui touche au mouvement NoEstimates. Mais c'est un autre sujet._ diff --git a/site/content/posts/2015-02-10-decouverte-amorcage-projet-planifier.md b/blog/_posts/2015-02-10-decouverte-amorcage-projet-planifier.md similarity index 93% rename from site/content/posts/2015-02-10-decouverte-amorcage-projet-planifier.md rename to blog/_posts/2015-02-10-decouverte-amorcage-projet-planifier.md index 5a525d2d6..0ce7cbdce 100644 --- a/site/content/posts/2015-02-10-decouverte-amorcage-projet-planifier.md +++ b/blog/_posts/2015-02-10-decouverte-amorcage-projet-planifier.md @@ -1,18 +1,18 @@ --- -title: "Découverte et amorçage de projet : planifier des capacités et des risques" +title: 'Découverte et amorçage de projet : planifier des capacités et des risques' author: Simon Denier date: 2015-02-10T15:21:00+00:00 image: /img/2015-02-storymap.jpg categories: - - DÉVELOPPEMENT + - Développement logiciel tags: - agile - capabilities - planning - project - risk - --- + Face à un porteur de projet en phase de démarrage, nous avons besoin d'outils pour : 1. comprendre son idée et sa valeur, @@ -24,16 +24,15 @@ Dans notre expérience, l'approche story map demande une certaine maturité des Dans [l'article précédent](https://blog.sogilis.com/posts/2015-02-03-decouverte-amorcage-projet-storymap/), nous avons expliqué pourquoi la story map n'était pas l'outil le plus adapté pour découvrir et planifier un nouveau projet. Bien que favorisant la discussion, le focus sur les activités des utilisateurs et les lots de fonctionnalité ne permet pas d'aborder les questions fondamentales du porteur de projet à ce stade : - -* la valeur proposée par le projet est-elle bien identifiée ? -* est-ce qu'on a repéré les problèmes clés ? -* comment procéder pour développer et tester au mieux les idées ? -* combien et comment investir ? +- la valeur proposée par le projet est-elle bien identifiée ? +- est-ce qu'on a repéré les problèmes clés ? +- comment procéder pour développer et tester au mieux les idées ? +- combien et comment investir ? A l'issue d'une story map, ceci se traduit malheureusement souvent par les questions suivantes : -* est-ce que la story map correspond à la liste des fonctionnalités ? Qu'est-ce qui va rentrer dans le périmètre final ? -* combien ça va coûter ? +- est-ce que la story map correspond à la liste des fonctionnalités ? Qu'est-ce qui va rentrer dans le périmètre final ? +- combien ça va coûter ? Ce qui amène immanquablement à des incompréhensions et des discussions commerciales sur le forfait de développement. **Ces questions ne rendent pas service à un projet en phase de démarrage, car elles figent trop les attentes.** Ce n'est pas le genre de collaboration que nous souhaitons mettre en place avec les porteurs de projet. Pour autant, par le passé, nous faisions avec et arrivions à fournir les réponses demandées. @@ -55,8 +54,8 @@ Le but de cet article n'est pas de présenter toutes les idées de Liz, rassembl Comme pour la story map, nous commençons par une vision globale. Le but est ici de faire un tour rapide de toutes les personnes impactées par le projet et de noter comment ou pourquoi elles seraient impactées. -* **Le porteur de projet (_primary stakeholder_) propose sa vision, son but :** l'idée qui apporte une nouvelle valeur, l'objectif à atteindre. C'est lui qui va défendre le projet, prendre les décisions, car il espère en tirer un bénéfice. -* Les acteurs secondaires (_secondary or incidental stakeholders_) ont des besoins ou buts que le projet va impacter. Ils peuvent se sentir concernés (ou non) par la vision du projet. Mais si jamais le projet devenait gênant pour leurs propres besoins, cela deviendrait un point de blocage. Bien qu'ils ne soient pas moteurs du projet, il faut donc prendre en compte leurs besoins. +- **Le porteur de projet (_primary stakeholder_) propose sa vision, son but :** l'idée qui apporte une nouvelle valeur, l'objectif à atteindre. C'est lui qui va défendre le projet, prendre les décisions, car il espère en tirer un bénéfice. +- Les acteurs secondaires (_secondary or incidental stakeholders_) ont des besoins ou buts que le projet va impacter. Ils peuvent se sentir concernés (ou non) par la vision du projet. Mais si jamais le projet devenait gênant pour leurs propres besoins, cela deviendrait un point de blocage. Bien qu'ils ne soient pas moteurs du projet, il faut donc prendre en compte leurs besoins. Les acteurs secondaires regroupent de nombreux profils. La majorité des futurs utilisateurs du système sont en fait des acteurs secondaires. On peut trouver aussi les intervenants financiers, des acteurs en amont ou en aval, ou encore les concurrents. On s'intéressera en particulier aux acteurs pour lesquels négliger les besoins ou buts conduiraient à l'échec du projet. @@ -66,10 +65,10 @@ La vision nous sert à identifier les acteurs les plus importants pour la réuss **Mais pour pouvoir représenter le système pour tout le monde et l'analyser, nous exprimons en fait ses capacités.** Celles-ci permettent de rendre compte que tel besoin ou but est bien pris en compte, sans rentrer dans le détail de la fonctionnalité ou de la solution : on parlera de la capacité à résoudre le problème A, à supporter l'activité B, sans être obligé de figer une fonctionnalité. Par exemple, on peut parler de capacité pour : -* visualiser des métriques -* gérer un planning -* comparer des produits entre eux -* commander des billets en ligne +- visualiser des métriques +- gérer un planning +- comparer des produits entre eux +- commander des billets en ligne Plus la capacité est importante et différenciante pour l'acteur, plus il est intéressant d'en discuter et d'être spécifique dans son intitulé. Plus la capacité est standard (une « commodité »), moins il est nécessaire de passer du temps dessus. Par exemple, la capacité à commenter sur des sites Internet est généralement survolé, car il existe des solutions toutes prêtes - à moins, bien sûr, que le projet porte justement sur une nouvelle approche des commentaires. @@ -91,9 +90,9 @@ Nous pouvons rentrer dans le vif du sujet, là où la méthode de Liz Keogh pren Prenons un système de suivi de compétition : -* différentiateur : le suivi des concurrents avec mixage GPS et caméra embarquée -* commodité : la sortie des résultats officiels -* spoiler : une fonction de replay après la course +- différentiateur : le suivi des concurrents avec mixage GPS et caméra embarquée +- commodité : la sortie des résultats officiels +- spoiler : une fonction de replay après la course **Logiquement, la vision du projet se concrétise dans un différenciateur.** Si cela ne semble pas assez différenciant, creusez encore : la différence n'est pas nécessairement exprimée par une nouvelle fonctionnalité, mais par une nouvelle approche des activités, un modèle de business alternatif… @@ -119,10 +118,10 @@ Par nature, la capacité différenciante du projet est celle que personne n'a ja Croiser la valeur différenciante et les risques des capacités nous permet donc de dresser un plan d'attaque. Reconsidérez votre cartographie des capacités : comment pouvez-vous la réaliser en cherchant la valeur et en éliminant les risques ? Peut-être le projet est-il assez simple pour être réalisé en une fois. Plus probablement, il y a beaucoup d'inconnus au tableau et vous avez besoin de planifier votre projet par étape. -* Il arrive que la capacité différenciante apparaisse trop compliquée pour être aboutie en une fois. Dans ce cas, il peut être possible de redécouper celle-ci en capacités moins différenciantes mais plus faciles à atteindre. -* Si votre capacité différenciante n'est pas notée comme risquée, rediscutez-la. Peut-être est-elle mal exprimée pour que les gens perçoivent le risque. Peut-être la vraie différence et le risque sont-ils dissimulés dans un certain aspect de la capacité, qu'il faut alors mettre en avant, extraire comme une capacité à part entière. -* Si vous avez plusieurs capacités différenciantes et risquées, essayez de les échelonner pour ne pas avoir plusieurs problèmes sur le feu. Il peut y avoir des priorités ou des dépendances entre capacités qui ordonneront les étapes. -* Et si certaines capacités nécessaires à votre projet sont risquées sans être différenciantes, posez-vous la question de faire baisser le risque sur cet aspect : se contenter d'une version allégée de la capacité, acheter un système tout prêt, proposer des solutions alternatives pour satisfaire la capacité. +- Il arrive que la capacité différenciante apparaisse trop compliquée pour être aboutie en une fois. Dans ce cas, il peut être possible de redécouper celle-ci en capacités moins différenciantes mais plus faciles à atteindre. +- Si votre capacité différenciante n'est pas notée comme risquée, rediscutez-la. Peut-être est-elle mal exprimée pour que les gens perçoivent le risque. Peut-être la vraie différence et le risque sont-ils dissimulés dans un certain aspect de la capacité, qu'il faut alors mettre en avant, extraire comme une capacité à part entière. +- Si vous avez plusieurs capacités différenciantes et risquées, essayez de les échelonner pour ne pas avoir plusieurs problèmes sur le feu. Il peut y avoir des priorités ou des dépendances entre capacités qui ordonneront les étapes. +- Et si certaines capacités nécessaires à votre projet sont risquées sans être différenciantes, posez-vous la question de faire baisser le risque sur cet aspect : se contenter d'une version allégée de la capacité, acheter un système tout prêt, proposer des solutions alternatives pour satisfaire la capacité. En répétant cette analyse sur les capacités, leur valeur et leur risque, les étapes de votre projet se dessinent peu à peu : **chaque étape vise à éliminer un risque tout en apportant une nouvelle valeur.** Vous concentrez votre effort sur l'essentiel. Les commodités et les fonctionnalités accessoires sont limités au strict minimum, tant que les risques essentiels ne sont pas cernés et maîtrisés. @@ -131,4 +130,3 @@ En répétant cette analyse sur les capacités, leur valeur et leur risque, les Un autre principe pour préparer votre plan d'attaque est de fixer des rendez-vous réguliers pour démontrer la valeur du projet. Quelle est la prochaine étape ? Quelle valeur pensez-vous pouvoir démontrer la prochaine fois ? Cela vaut-il le coup ? **Quand vous pouvez décrire de quoi sera capable la prochaine version du système et que vous êtes confiants dans votre capacité de le réaliser, alors vous êtes prêts pour démarrer le projet !** Après cette présentation générale de la méthode, nous verrons dans le prochain article quelques leçons issues de notre expérience. Nous pourrons les comparer avec [l'approche story map](https://blog.sogilis.com/posts/2015-02-03-decouverte-amorcage-projet-storymap/) et voir comment cela affecte notre vision de l'accompagnement agile. - diff --git a/site/content/posts/2015-02-19-decouverte-et-amorcage-de-projet-quavons-nous.md b/blog/_posts/2015-02-19-decouverte-et-amorcage-de-projet-quavons-nous.md similarity index 94% rename from site/content/posts/2015-02-19-decouverte-et-amorcage-de-projet-quavons-nous.md rename to blog/_posts/2015-02-19-decouverte-et-amorcage-de-projet-quavons-nous.md index 65c51e6a9..98e1ac406 100644 --- a/site/content/posts/2015-02-19-decouverte-et-amorcage-de-projet-quavons-nous.md +++ b/blog/_posts/2015-02-19-decouverte-et-amorcage-de-projet-quavons-nous.md @@ -4,7 +4,7 @@ author: Simon Denier date: 2015-02-19T15:46:00+00:00 image: /img/2016-04-2.Produits.jpg categories: - - DÉVELOPPEMENT + - Développement logiciel tags: - agile - capabilities @@ -12,10 +12,9 @@ tags: - project - risk - storymap - --- -Dans ce dernier article, nous allons revoir les [problèmes rencontrés avec les story maps](https://blog.sogilis.com/posts/2015-02-03-decouverte-amorcage-projet-storymap/) et expliquer comment la planification par capacités apporte des réponses plus pertinentes dans notre contexte. Cette rétrospective nous montrera que l'essentiel n'est pas dans la méthode de Liz, mais bien dans sa façon d'approcher et de prioriser les problèmes - favoriser [« les individus et leurs interactions plus que les processus et les outils »](http://agilemanifesto.org/iso/fr/manifesto.html). Ceci nous mènera naturellement à une petite réflexion sur notre approche de l'agilité avec nos porteurs de projet. +Dans ce dernier article, nous allons revoir les [problèmes rencontrés avec les story maps](https://blog.sogilis.com/posts/2015-02-03-decouverte-amorcage-projet-storymap/) et expliquer comment la planification par capacités apporte des réponses plus pertinentes dans notre contexte. Cette rétrospective nous montrera que l'essentiel n'est pas dans la méthode de Liz, mais bien dans sa façon d'approcher et de prioriser les problèmes - favoriser [« les individus et leurs interactions plus que les processus et les outils »](http://agilemanifesto.org/iso/fr/manifesto.html). Ceci nous mènera naturellement à une petite réflexion sur notre approche de l'agilité avec nos porteurs de projet. ## Quelques leçons tirées de la cartographie par capacité @@ -31,28 +30,28 @@ La vision du projet, avec sa proposition de valeur mais aussi son impact sur les Le focus trop rapide sur les fonctionnalités peut aussi avoir des effets négatifs lors de la réalisation du projet. -* Le besoin de la fonctionnalité est peu approfondie ; il n'en reste qu'une description superficielle, de laquelle il est difficile de revenir aux sources si on cherche une solution alternative. -* La liste des fonctionnalités de la story map apparaît parfois comme un périmètre, à la manière d'un cahier des charges. Or beaucoup de choses peuvent encore changer en cours de projet. -* La granularité des fonctionnalités peut être très hétérogène et leur description aussi : les « petites » fonctionnalités auront tendance à être plus détaillées que les grosses, car plus évidentes ; conjointement la complexité des grosses peut être masquée par le manque de détails dans leur description. -* Des notions de valeur et de coût sont parfois utilisées pour aider à la priorisation des fonctionnalités. Ces notions posent des problèmes de définition (s'agit-il de la valeur en tant que nouveauté ou nécessité ? Quelle est l'échelle pour l'estimation du coût : _story points_, jour-hommes ?) Se focaliser sur ces estimations peut masquer des problèmes de dépendances : la fonctionnalité X n'est pas jugée intéressante car coûteuse, pourtant une sous-partie est nécessaire à une autre partie valorisable. +- Le besoin de la fonctionnalité est peu approfondie ; il n'en reste qu'une description superficielle, de laquelle il est difficile de revenir aux sources si on cherche une solution alternative. +- La liste des fonctionnalités de la story map apparaît parfois comme un périmètre, à la manière d'un cahier des charges. Or beaucoup de choses peuvent encore changer en cours de projet. +- La granularité des fonctionnalités peut être très hétérogène et leur description aussi : les « petites » fonctionnalités auront tendance à être plus détaillées que les grosses, car plus évidentes ; conjointement la complexité des grosses peut être masquée par le manque de détails dans leur description. +- Des notions de valeur et de coût sont parfois utilisées pour aider à la priorisation des fonctionnalités. Ces notions posent des problèmes de définition (s'agit-il de la valeur en tant que nouveauté ou nécessité ? Quelle est l'échelle pour l'estimation du coût : _story points_, jour-hommes ?) Se focaliser sur ces estimations peut masquer des problèmes de dépendances : la fonctionnalité X n'est pas jugée intéressante car coûteuse, pourtant une sous-partie est nécessaire à une autre partie valorisable. Bref, la story map peut donner une fausse impression de complétude, à la manière d'un cahier des charges, avec différents lots de fonctionnalités définis à l'avance. En cas de problème lors de la réalisation, le champ d'action pour rétablir le projet peut être limité car les acteurs auront du mal à sortir des rails de la story map. **Tout projet propose une vision différente par rapport à l'existant. Mais il existe plusieurs façons de réaliser cette vision.** L'important est donc de ne pas avoir d'oeillères sur le chemin de la réalisation. Parler de capacités doit permettre de se concentrer sur le problème et le besoin à résoudre. -* En prenant une approche centrée sur le problème, il y a moins de risque de passer du temps sur les détails d'une solution (la fonctionnalité) au détriment d'autres possibilités. -* En exprimant les futures _capacités_ du système, vous savez que vous devez chercher une solution pour satisfaire ces capacités. Les moyens de les satisfaire sont multiples et peuvent être remises en cause en cours de projet (nouvelles options, solutions alternatives et moins coûteuses). Le terme de capacité permet aussi plus facilement d'aborder les aspects non fonctionnels du système (robustesse, performance…). -* En cas d'échec avec une solution, il est plus facile de repartir de la capacité que d'une fonctionnalité (quel est le problème, le besoin ? Comment y répondre ?). +- En prenant une approche centrée sur le problème, il y a moins de risque de passer du temps sur les détails d'une solution (la fonctionnalité) au détriment d'autres possibilités. +- En exprimant les futures _capacités_ du système, vous savez que vous devez chercher une solution pour satisfaire ces capacités. Les moyens de les satisfaire sont multiples et peuvent être remises en cause en cours de projet (nouvelles options, solutions alternatives et moins coûteuses). Le terme de capacité permet aussi plus facilement d'aborder les aspects non fonctionnels du système (robustesse, performance…). +- En cas d'échec avec une solution, il est plus facile de repartir de la capacité que d'une fonctionnalité (quel est le problème, le besoin ? Comment y répondre ?). ### La planification est basée sur le dérisquage et la valorisation du projet, pas sur la priorisation par estimation La pratique courante issue des story maps et des méthodologies agiles est de prioriser les stories par rapport à leur valeur et leur coût estimé, pour les placer dans l'ordre dans le backlog. La notion de chemin critique, voire de MVP, est parfois abordée mais pas nécessairement approfondie. Le backlog ressemble alors à une liste de fonctionnalités à développer. -* Le suivi se base sur la réalisation d'un plan (même s'il est léger) et il peut se créer un ressenti de retard ou de travail mal fait à cause des sacrifices faits par rapport au plan (fonctionnalité ou qualité). -* La priorisation liée aux dépendances entre fonctionnalités est souvent masquée dans cette approche ; cela peut rendre la repriorisation complexe. -* Si une fonctionnalité se révèle mal estimée en cours de sprint, des décalages liés à la résolution des problèmes se créent dans le suivi de charge, ce qui peut être perturbant pour une équipe mal rôdée ; l'équipe pense prendre du retard, qui ne peut être rattrapé sauf à changer le périmètre. -* Même tard en fin de projet, on peut tomber sur une exigence complexe, mal estimée, qui rend difficile la vision sur la fin du projet. -* Plus on est ignorant sur un aspect du projet, moins on sera pertinent pour son estimation, plus on prend des risques à s'engager. Or les plans type story map tendent malgré tout à un engagement, même peu formalisé. +- Le suivi se base sur la réalisation d'un plan (même s'il est léger) et il peut se créer un ressenti de retard ou de travail mal fait à cause des sacrifices faits par rapport au plan (fonctionnalité ou qualité). +- La priorisation liée aux dépendances entre fonctionnalités est souvent masquée dans cette approche ; cela peut rendre la repriorisation complexe. +- Si une fonctionnalité se révèle mal estimée en cours de sprint, des décalages liés à la résolution des problèmes se créent dans le suivi de charge, ce qui peut être perturbant pour une équipe mal rôdée ; l'équipe pense prendre du retard, qui ne peut être rattrapé sauf à changer le périmètre. +- Même tard en fin de projet, on peut tomber sur une exigence complexe, mal estimée, qui rend difficile la vision sur la fin du projet. +- Plus on est ignorant sur un aspect du projet, moins on sera pertinent pour son estimation, plus on prend des risques à s'engager. Or les plans type story map tendent malgré tout à un engagement, même peu formalisé. En se focalisant sur les risques d'échec ainsi que sur les points différenciants, l'accent est mis sur l'essentiel : trouver la prochaine étape la plus sûre et la plus rapide pour démontrer la valeur du projet, avant de passer à la suivante. **L'approche vise la réalisation de la vision**, la résolution des problèmes rencontrés, plutôt que la coche de fonctionnalités dans une liste. diff --git a/site/content/posts/2015-02-24-ansible-partie-1-base.md b/blog/_posts/2015-02-24-ansible-partie-1-base.md similarity index 93% rename from site/content/posts/2015-02-24-ansible-partie-1-base.md rename to blog/_posts/2015-02-24-ansible-partie-1-base.md index efc307098..f40e3c700 100644 --- a/site/content/posts/2015-02-24-ansible-partie-1-base.md +++ b/blog/_posts/2015-02-24-ansible-partie-1-base.md @@ -1,24 +1,24 @@ --- -title: "Ansible partie 1 : La Base" +title: 'Ansible partie 1 : La Base' author: Sogilis date: 2015-02-24T12:54:14+00:00 image: /img/2016-05-Sogilis-Christophe-Levet-Photographe-8824-e1462795824232.jpg categories: - - DÉVELOPPEMENT + - Développement logiciel tags: - ansible - devops - --- + ## La gestion de configuration Ansible est un outil de déploiement automatique, il permet de facilement et surtout de manière reproductible, provisionner une machine. De manière concrète, cela permet : -* d'installer des paquets -* d'installer des fichiers de configuration -* de configurer les utilisateurs systèmes -* de configurer les services -* de réaliser n'importe quelle tâche d'administration que l'on pourrait réaliser par ssh +- d'installer des paquets +- d'installer des fichiers de configuration +- de configurer les utilisateurs systèmes +- de configurer les services +- de réaliser n'importe quelle tâche d'administration que l'on pourrait réaliser par ssh La gestion automatique du déploiement est cruciale pour permettre d'avoir un serveur dans un état connu. La configuration manuelle d'un serveur est bien pour apprendre, mais au bout de quelques mois en production, nous oublions tous ce que nous y avons fait. Il est donc très important de consigner ces opérations quelque part. @@ -34,34 +34,34 @@ Différents outils fonctionnent différemment. En ce qui concerne Ansible, ou pl Notre premier hello world va utiliser deux fichiers. Vous avez d'abord besoin de décrire votre _playbook_. Ansible utilise une syntaxe YAML. Notre premier _playbook_ va être : -{{< highlight yml >}} +````yml --- - hosts: perrin   sudo: yes   tasks:     - shell: echo Hello World > /tmp/hello -{{< /highlight >}} +``` Vous le constatez peut-être, mais ce playbook nous indique que nous voulons nous connecter au serveur appelé perrin, que nous utiliserons sudo afin de gagner les droits administrateur, et que notre seule tâche sera d'exécuter une commande shell permettant d'écrire _Hello World_ dans `/tmp/hello`. La seule chose que nous n'avons pas décrite, c'est comment accéder au serveur perrin. Pour cela, nous devons rédiger un fichier `hosts` comme celui-ci : -{{< highlight ini >}} +```ini [perrin] perrin.mildred.fr ansible_ssh_user=admin -{{< /highlight >}} +``` Il nous indique que dans le groupe perrin, nous avons un seul serveur dont le nom de domaine est `perrin.mildred.fr` et que le nom d'utilisateur pour s'y connecter en ssh est `admin`. Si nous voulions configurer plusieurs serveurs ensemble, il aurait été possible d'indiquer d'autres adresses sur les lignes suivantes. Pour exécuter ce playbook sur ce serveur, la commande est la suivante : -{{< highlight bash >}} +```bash ansible-playbook -i hosts hello/hello.yml -{{< /highlight >}} +``` Dont le résultat nous donne : -{{< highlight bash >}} +```bash _______________ < PLAY [perrin] > --------------- @@ -101,11 +101,11 @@ changed: [perrin.mildred.fr] || || perrin.mildred.fr : ok=2 changed=1 unreachable=0 failed=0 -{{< /highlight >}} +``` Si comme moi, vous trouvez cette sortie peu lisible, je vous encourage a indiquer dans votre environnement (dans `~/.zshenv`, `~/.bashrc` ou `~/.profile`) la variable d'environnement `ANSIBLE_NOCOWS=1`. N'oubliez pas de l'exporter avec le mot clef `export`. Vous devriez alors voir la sortie suivante : -{{< highlight bash >}} +```bash PLAY [perrin] ***************************************************************** GATHERING FACTS *************************************************************** @@ -116,7 +116,7 @@ changed: [perrin.mildred.fr] PLAY RECAP ******************************************************************** perrin.mildred.fr : ok=2 changed=1 unreachable=0 failed=0 -{{< /highlight >}} +``` Nous pouvons visualiser les différentes tâches qui sont exécutées. Les tâches peuvent être dans plusieurs états, et cela est comptabilisé en fin d'exécution : @@ -126,3 +126,4 @@ Nous pouvons visualiser les différentes tâches qui sont exécutées. Les tâch * unreachable : une précédente erreur a empêché l'exécution de cette tâche Maintenant, vous êtes prêts à construire votre propre _playbook_. Nous n'avons pour le moment que survolé le module shell (qui nous permet d'exécuter des commandes shell), mais il existe plein d'autres modules. La documentation de ces modules est très bien faite, il faut s'y référer. Dans un prochain article, nous verrons comment construire facilement des modules personnalisés dans le cas où les modules proposés sont insuffisants. +```` diff --git a/site/content/posts/2015-03-03-ansible-creer-module-simple.md b/blog/_posts/2015-03-03-ansible-creer-module-simple.md similarity index 92% rename from site/content/posts/2015-03-03-ansible-creer-module-simple.md rename to blog/_posts/2015-03-03-ansible-creer-module-simple.md index ed7874cca..26286d9d9 100644 --- a/site/content/posts/2015-03-03-ansible-creer-module-simple.md +++ b/blog/_posts/2015-03-03-ansible-creer-module-simple.md @@ -1,23 +1,23 @@ --- -title: "Ansible partie 2 : Créer un module simple pour installer des projets Go" +title: 'Ansible partie 2 : Créer un module simple pour installer des projets Go' author: Sogilis date: 2015-03-03T12:26:06+00:00 image: /img/2016-04-2.Developpement-e1460448074336.jpg categories: - - DÉVELOPPEMENT + - Développement logiciel tags: - ansible - bash - devops - shell - --- + La dernière fois, nous avions présenté le fonctionnement de base d'Ansible. Comment il était possible de définir une liste de tâches dans un _playbook_ afin de les exécuter sur un serveur. Nous avions vu qu'il existait bon nombre de modules fournis de base permettant de réaliser des tâches très diverses. Mais que faire, si on veut faire quelque chose qui n'est pas prévu dans les modules prédéfinis ? Deux solutions : -* A coup de tâches shell on exécute les commandes nécessaires, fastidieux, et peu maintenable. -* On réalise un programme séparé (sous la forme d'un script shell dans notre exemple) qui va exécuter la tâche désignée et va s'interfacer en tant que module avec Ansible. +- A coup de tâches shell on exécute les commandes nécessaires, fastidieux, et peu maintenable. +- On réalise un programme séparé (sous la forme d'un script shell dans notre exemple) qui va exécuter la tâche désignée et va s'interfacer en tant que module avec Ansible. Un des grands avantages d'Ansible est de permettre de créer des modules dans n'importe quel langage de programmation. Il suffit que votre code soit exécutable, l'interface avec Ansible se fait via des paramètres en ligne de commande et la sortie texte du programme. Si le shell est plus adapté, on peut écrire du shell. Si on aime le Python, c'est possible. Le Ruby, bien sûr. Perl, Lua ou même en C, pourquoi pas. @@ -25,33 +25,33 @@ Un des grands avantages d'Ansible est de permettre de créer des modules dans n' Un module Ansible s'utilise comme suit : -{{< highlight yml >}} +````yml - module_name: parameters -{{< /highlight >}} +``` `module_name` étant le nom du module, _`parameters`_ étant une chaîne de caractères libre, généralement sous la forme `clef=valeur`, ceci n'étant pas obligatoire. Lorsqu'on utilise la forme `clef=valeur`, il est possible d'utiliser les modules avec une autre syntaxe un peu plus détaillée : -{{< highlight yml >}} +```yml - module_name: key1: value1key2: value2 -{{< /highlight >}} +``` Ceci est strictement équivalent à écrire : -{{< highlight yml >}} +```yml - module_name: key1=value1 key2=value2 -{{< /highlight >}} +``` Lorsque ce module sera appelé, un programme au doux nom de `module_name` sera exécuté sur le serveur à déployer avec comme premier paramètre de ligne de commande un fichier contenant la ligne`key1=value1 key2=value2`. Le programme n'a qu'à lire ce fichier pour comprendre ce qu'il doit faire, et pour les scripts shell, c'est simple, parce que c'est la syntaxe shell de définition de variables. Il suffit donc de sourcer ce fichier. Pour indiquer le résultat, il doit écrire sur la sortie standard ou d'erreur indifféremment un fichier JSON contenant des variables résultat, par exemple : -{{< highlight json >}} +```json { "failed": false, "changed": false } -{{< /highlight >}} +``` Ceci indique à Ansible qu'il n'y a pas eu d'erreur (variable `failed`), que le système n'a pas été changé car il était déjà configuré (variable `changed`). Si la sortie du programme n'est pas valide JSON, le module sera considéré comme ayant échoué. @@ -59,11 +59,11 @@ Ceci indique à Ansible qu'il n'y a pas eu d'erreur (variable `failed`), que le Pour les besoins de cet article, nous allons faire un module qui installe un programme écrit dans le [langage Go](http://golang.org/). Une fois qu'on a `go` installé, il est très simple et rapide de compiler un programme Go. En effet, tout est généralement compilé statiquement sans dépendances externes, et l'outil `go` permet de récupérer récursivement toutes les sources nécessaires à un programme. Par exemple, pour installer le projet [GitHub go-ipfs](https://github.com/jbenet/go-ipfs), il suffit d'exécuter les commandes suivantes : -{{< highlight bash >}} +```bash go get github.com/jbenet/go-ipfs/cmd/ipfs go build github.com/jbenet/go-ipfs/cmd/ipfs go install github.com/jbenet/go-ipfs/cmd/ipfs -{{< /highlight >}} +``` Ceci va télécharger les sources dans  `$GOPATH/src`, compiler dans `$GOPATH/pkg` et installer le programme ipfs dans `$GOPATH/bin`. @@ -73,7 +73,7 @@ Une autre astuce réside dans l'installation dans `/usr/local` en utilisant [sto Par exemple, si on considère le langage [Lua](http://www.lua.org/) installé, nous trouvons les liens symboliques suivants : -{{< highlight bash >}} +```bash lrwxrwxrwx 1 root staff 21 août 6 14:17 /usr/local/bin/lua -> ../stow/lua52/bin/lua lrwxrwxrwx 1 root staff 22 août 6 14:17 /usr/local/bin/luac -> ../stow/lua52/bin/luac lrwxrwxrwx 1 root staff 31 mai 13 2014 /usr/local/include/lauxlib.h -> ../stow/lua52/include/lauxlib.h @@ -84,7 +84,7 @@ lrwxrwxrwx 1 root staff 30 mai 13 2014 /usr/local/include/lualib.h -> ../s lrwxrwxrwx 1 root staff 26 mai 13 2014 /usr/local/lib/liblua.a -> ../stow/lua52/lib/liblua.a -rw-r--r-- 1 root staff 2204 nov. 16 2011 /usr/local/share/man/man1/lua.1 -rw-r--r-- 1 root staff 3071 nov. 16 2011 /usr/local/share/man/man1/luac.1 -{{< /highlight >}} +``` Cela nécessite d'installer les programmes avec un préfixe particulier. Généralement cela se fait avec `./configure –prefix=/usr/local/stow/progname` ou `cmake -DCMAKE_INSTALL_PREFIX=/usr/local/stow/progname`. @@ -92,7 +92,7 @@ Cela nécessite d'installer les programmes avec un préfixe particulier. Génér Une première version du module pourrait être la suivante : -{{< highlight ini >}} +```ini #!/bin/sh exec 3>&1 >/dev/null 2>&1 . "$1" @@ -118,13 +118,13 @@ cat <&3 } EOF exit 0 -{{< /highlight >}} +``` Ce module s'utilise ainsi : -{{< highlight bash >}} +```bash - go-install: name=go-ipfs package=github.com/jbenet/go-ipfs/cmd/ipfs -{{< /highlight >}} +``` Ce script va d'abord supprimer le dossier `bin`, créer un dossier `/usr/local/stow/$name/bin` et faire pointer `/usr/local/src/$name/bin` vers ce dernier dossier. Ainsi `go install` placera directement les fichiers dans `/usr/local/stow/$name/bin`. @@ -136,7 +136,7 @@ Ne pas oublier la commande `exec 3>&1 >/dev/null 2>&1` qui ferme la sortie stand Afin de gérer les erreurs, le cas où les commandes exécutées retournent un code d'erreur différent de zéro, nous allons utiliser bash au lieu du vénérable shell POSIX sh et utiliser la commande [trap](http://tldp.org/LDP/Bash-Beginners-Guide/html/sect_12_02.html). Nous allons aussi initialiser les variables dans le cas où notre environnement n'est pas propre, et donner une valeur par défaut à la variable `$name` : -{{< highlight ini >}} +```ini #!/bin/bash failed=false @@ -173,31 +173,31 @@ cat <&3 } EOF exit $res_code -{{< /highlight >}} +``` ## Utilisation du module Pour utiliser le module, il doit être placé dans un dossier `library/` au coté du _playbook_. Nous avons donc les fichiers suivants : -{{< highlight bash >}} +```bash ipfs.yml hosts library/go-install -{{< /highlight >}} +``` Notre _playbook_ `ipfs.yml` pourrait être ainsi : -{{< highlight yml >}} +```yml --- - hosts: perrin   sudo: yes   tasks: - go-install: name=go-ipfs package=github.com/jbenet/go-ipfs/cmd/ipfs -{{< /highlight >}} +``` L'exécution donne ceci : -{{< highlight bash >}} +```bash PLAY [perrin] ***************************************************************** GATHERING FACTS *************************************************************** @@ -208,6 +208,7 @@ changed: [perrin.mildred.fr] PLAY RECAP ******************************************************************** perrin.mildred.fr : ok=2 changed=1 unreachable=0 failed=0 -{{< /highlight >}} +``` Vous avez maintenant pu voir le fonctionnement des modules Ansible, je vous encourage à jouer avec. Si vous étiez à l'aise avec le shell, et redoutiez les outils de déploiement automatique — parce-qu'il faut l'avouer, le langage est parfois bien plus compliqué — vous êtes maintenant armé pour utiliser Ansible en toute simplicité. Dans un prochain article, nous verrons comment structurer des _playbooks_ Ansible lorsque vous souhaitez mixer différentes tâches. +```` diff --git a/site/content/posts/2015-03-10-ansible-partie-3-roles.md b/blog/_posts/2015-03-10-ansible-partie-3-roles.md similarity index 93% rename from site/content/posts/2015-03-10-ansible-partie-3-roles.md rename to blog/_posts/2015-03-10-ansible-partie-3-roles.md index b8c341186..c9c1ce104 100644 --- a/site/content/posts/2015-03-10-ansible-partie-3-roles.md +++ b/blog/_posts/2015-03-10-ansible-partie-3-roles.md @@ -1,60 +1,60 @@ --- -title: "Ansible partie 3 : Les rôles" +title: 'Ansible partie 3 : Les rôles' author: Sogilis date: 2015-03-10T13:03:36+00:00 image: /img/2015-03-Sogilis-Christophe-Levet-Photographe-7503.jpg categories: - - DÉVELOPPEMENT + - Développement logiciel tags: - ansible - devops - roles - --- + Dans notre précédent article, nous avons vu comment installer l'application [ipfs](https://github.com/jbenet/go-ipfs) sur notre serveur. Nous avons fait le tout de manière très simple, avec un playbook global. Cela est peu élégant si nous souhaitons déployer plusieurs services sur la même machine. Et comment faire pour découper une suite de tâches simples que nous voudrions pouvoir réutiliser ? La réponse à ces deux questions se trouve dans les rôles Ansible. Les rôles sont une manière un peu plus élégante d'inclure des tâches Ansible au sein d'autres tâches en déclarant des dépendances. ## Transformer notre playbook en rôle Commençons par créer un rôle simple correspondant exactement au playbook que nous avions la dernière fois : -{{< highlight yml >}} +````yml --- - hosts: perrin   sudo: yes   tasks: - go-install: name=go-ipfs package=github.com/jbenet/go-ipfs/cmd/ipfs -{{< /highlight >}} +``` Le rôle que nous allons créer va nécessiter de créer une arborescence dans un dossier `roles/` qui va contenir notre role nommé `ipfs` : * `roles/ipfs/tasks/main.yml` : - {{< highlight yml >}} + ```yml --- - go-install: name=go-ipfs package=github.com/jbenet/go-ipfs/cmd/ipfs - {{< /highlight >}} + ``` Notre nouveau playbook va maintenant contenir une dépendance envers ce rôle : -{{< highlight yml >}} +```yml --- - hosts: perrin   sudo: yes   roles: - ipfs -{{< /highlight >}} +``` ## Un second rôle : cjdns-docker Ce qui est intéressant, c'est d'avoir plusieurs rôles. Nous allons donc voir comment déployer cjdns en utilisant docker avec un rôle. * Ce rôle définit des variables dans `roles/cjdns-docker/vars/main.yml` : - {{< highlight yml >}} + ```yml --- name: cjdns - {{< /highlight >}} + ``` * et se compose de tâches définies dans un fichier `roles/cjdns-docker/tasks/main.yml` : - {{< highlight yml >}} + ```yml --- - shell: docker pull mildred/cjdns - systemd-docker-service: name='{{name}}' @@ -62,7 +62,7 @@ Ce qui est intéressant, c'est d'avoir plusieurs rôles. Nous allons donc voir c - docker-datadir: name='{{name}}' volume=/etc/cjdns file=/cjdroute.conf register: cjdroute_conf - file: src='{{cjdroute_conf.file}}' dest='/etc/docker-{{name}}.conf' state=link - {{< /highlight >}} + ``` La variable `name` permet de nommer différents éléments du système correspondant au rôle. Elle a une valeur par défaut, mais pourra se redéfinir au niveau du playbook principal. Pour provisionner le container cjdns, il nous faut : @@ -78,7 +78,7 @@ Les modules peuvent définir des variables de leur propre chef, mais il est éga L'utilisation de ce rôle dans le playbook principal nécessite de définir une variable au moment de l'inclusion du rôle : -{{< highlight yml >}} +```yml --- - hosts: perrin sudo: yes @@ -88,12 +88,12 @@ L'utilisation de ce rôle dans le playbook principal nécessite de définir une - ipfs - role: cjdns-docker name: '{{name}}-cjdroute' -{{< /highlight >}} +``` Si vous tentez d'exécuter ce role, il vous manquera les modules `systemd-docker-service` et `docker-datadir`. Leur conception n'implique rien de nouveau et voici leur code : * `library/systemd-docker-service` : - {{< highlight ini >}} + ```ini #!/bin/bash changed=false @@ -171,19 +171,19 @@ Si vous tentez d'exécuter ce role, il vous manquera les modules `systemd-docker } EOF exit $res_code - {{< /highlight >}} + ``` * `library/docker-datadir` : - {{< highlight ini >}} + ```ini #!/bin/bash changed=false failed=false res_code=0 msg=Success - {{< /highlight >}} + ``` - {{< highlight yml >}} + ```yml --- - shell: docker pull mildred/cjdns - systemd-docker-service: name='{{name}}' @@ -191,9 +191,9 @@ Si vous tentez d'exécuter ce role, il vous manquera les modules `systemd-docker - docker-datadir: name='{{name}}' volume=/etc/cjdns file=/cjdroute.conf register: cjdroute_conf - file: src='{{cjdroute_conf.file}}' dest='/etc/docker-{{name}}.conf' state=link - {{< /highlight >}} + ``` - {{< highlight bash >}} + ```bash exec 3>&1 >/dev/null 2>&1 trap 'failed=true res_code=1 msg="Failed at line $LINENO"' ERR @@ -217,7 +217,7 @@ Si vous tentez d'exécuter ce role, il vous manquera les modules `systemd-docker } EOF exit $res_code - {{< /highlight >}} + ``` ## Un dernier rôle pour accéder par ssh à notre container @@ -226,32 +226,32 @@ Nous voudrions pouvoir accéder au container docker en utilisant SSH avec un uti Nous aurons besoin de `nsenter`, donc nous [l'installons avec docker](http://jpetazzo.github.io/2014/06/23/docker-ssh-considered-evil/). Ensuite, nous créons un utilisateur avec l'UID 0 (afin qu'il ait les permissions d'exécuter `nsenter`), et nous spécifions une clef ssh de login, avec la commande `nsenter` qui nous permettra d'entrer dans le container : * `roles/docker-ssh/vars/main.yml` : - {{< highlight yml >}} + ```yml --- user: '{{name}}' shell: /bin/bash - {{< /highlight >}} + ``` * `roles/docker-ssh/tasks/main.yml` : - {{< highlight yml >}} + ```yml --- - command: docker run --rm -v /usr/local/bin:/target jpetazzo/nsenter - user: name="{{user}}" uid=0 createhome=yes shell=/usr/sbin/nologin home='/home/{{user}}' - authorized_key: key="{{ssh_key}}" user='{{user}}' key_options='command="nsenter --target $(docker inspect --format {{ "{{.State.Pid}}" }} {{name}}) --mount --uts --ipc --net --pid {{shell}}"' - {{< /highlight >}} + ``` Pour utiliser ce nouveau rôle, nous allons le déclarer comme dépendance dans `roles/cjdns-docker/meta/main.yml` : -{{< highlight yml >}} +```yml --- dependencies: - role: docker-ssh when: "'{{ssh_key}}' != ''" -{{< /highlight >}} +``` Et notre playbook principal va être augmenté afin de définir la variable `ssh_key` pour le rôle docker-cjdns (qui sera ensuite héritée par le rôle docker-ssh instancié par dépendance) : -{{< highlight yml >}} +```yml --- - hosts: perrin sudo: yes @@ -263,6 +263,7 @@ Et notre playbook principal va être augmenté afin de définir la variable `ssh - role: cjdns-docker name: '{{name}}-cjdroute' ssh_key: '{{admin_sshkey}}' -{{< /highlight >}} +``` Nous avons vu dans cet article comment organiser notre code Ansible afin qu'il soit plus maintenable. Les rôles définissent des unités de code comme pourraient l'être des fonctions dans un langage plus classique. Il n'est pas possible d'invoquer un rôle directement au milieu d'une liste de tâches [dans ce cas, (la directive include](http://docs.ansible.com/playbooks_roles.html) et [le module _include vars_](https://docs.ansible.com/ansible/latest/collections/ansible/builtin/include_vars_module.html) existent), mais il est possible de définir des dépendances entre rôles. En guise d'exercice, il est laissé au soin du lecteur de décomposer le module `systemd-docker-service` en un rôle séparé. +```` diff --git a/site/content/posts/2015-03-24-lecture-google-sheet-ruby.md b/blog/_posts/2015-03-24-lecture-google-sheet-ruby.md similarity index 94% rename from site/content/posts/2015-03-24-lecture-google-sheet-ruby.md rename to blog/_posts/2015-03-24-lecture-google-sheet-ruby.md index 9397dee14..ff3979585 100644 --- a/site/content/posts/2015-03-24-lecture-google-sheet-ruby.md +++ b/blog/_posts/2015-03-24-lecture-google-sheet-ruby.md @@ -4,7 +4,7 @@ author: Sogilis date: 2015-03-24T12:54:22+00:00 image: /img/2015-03-Sogilis-Christophe-Levet-Photographe-7461.jpg categories: - - DÉVELOPPEMENT + - Développement logiciel tags: - google - google drive @@ -14,15 +14,15 @@ tags: - ruby - service account - token - --- + Pour un développeur, piloter des Google Docs depuis son application est une fonctionnalité relativement puissante. Les API Google permettent beaucoup de choses en ce sens. En revanche, ce n'est pas toujours facile de s'y retrouver dans les méandres des différentes technologies, API et documentations que l'on peut trouver sur la toile. Suite aux difficultés que j'ai pu rencontrer en cherchant une documentation claire, je vais essayer ici de synthétiser les différentes étapes permettant de réaliser une opération assez fondamentale : **lire le contenu d’un document Google Sheet en utilisant une connexion authentifiée avec OAuth 2 en mode server to server**. !(Lecture d'un un fichier Google Sheet en Ruby[/img/2015-03-Oauth2GoogleDocs.png] Pour les pressés, voici le code Ruby qui fait le boulot : -{{< highlight ruby >}} +````ruby require 'rubygems' require 'google/api_client' require 'google_drive' @@ -57,7 +57,7 @@ session = GoogleDrive.login_with_oauth(token) document_id = "xxxyyyzzz" # Key attribute in Google drive url ws = session.spreadsheet_by_key(document_id).worksheets[0] puts ws[1,2] # Write content of cell A2 -{{< /highlight >}} +``` Mais passons à la première étape : l’authentification. @@ -102,26 +102,26 @@ Pour **en créer un**, il faut passer par la [Google Developers Console](https:/ Ensuite, il ne reste plus qu’à coder avec tous ces éléments : * [Installation de la gem - google drive](https://github.com/gimite/google-drive-ruby) - {{< highlight bash >}} + ```bash sudo gem install google_drive - {{< /highlight >}} + ``` * Ajout des librairies qui vont bien - {{< highlight ruby >}} + ```ruby require 'rubygems' require 'google/api_client' require 'google_drive' - {{< /highlight >}} + ``` * Création de la clé d’activation avec le fichier .p12 et le mot de passe récupéré lors de la création du compte de service - {{< highlight ruby >}} + ```ruby key_file = 'private-key-for-xxxxxx.p12' key_password = 'notasecret' key = Google::APIClient::KeyUtils.load_from_pkcs12(key_file, key_password) - {{< /highlight >}} + ``` * Authentification - {{< highlight ruby >}} + ```ruby client = Google::APIClient.new( :application_name => 'my_application_name', :application_version => '0.0.1') @@ -134,7 +134,7 @@ Ensuite, il ne reste plus qu’à coder avec tous ces éléments : + 'https://spreadsheets.google.com/feeds/', :issuer => account_email, :signing_key => key) - {{< /highlight >}} + ``` L’attribut « scope » contient les noms de domaine associés aux services Google auxquels on pourra accéder ensuite. @@ -143,15 +143,15 @@ Vous pouvez trouver la liste des scopes avec l’[OAuth 2.0 Playground](https:// Les autres attributs sont des valeurs fixes fournies par Google. * Récupération du jeton - {{< highlight ruby >}} + ```ruby client.authorization.fetch_access_token! token = client.authorization.access_token - {{< /highlight >}} + ``` * Connexion avec ce jeton - {{< highlight ruby >}} + ```ruby session = GoogleDrive.login_with_oauth(token) - {{< /highlight >}} + ``` Il est temps de passer à la seconde étape, la juridiction. @@ -175,7 +175,7 @@ Enfin, pour lire un document, on peut vouloir le faire à partir de l’**identi On peut trouver cet identifiant dans l’url lorsqu’on édite le document dans son navigateur. Il correspond à l’attribut _key_. -Prenons par exemple l’url suivante : +Prenons par exemple l’url suivante : `https://docs.google.com/a/sogilis.com/spreadsheet/ccc?key=0Ag7vwNTdThiNdDNNecDYclUsMzZ1R0JpbXdUaERMUVE&usp=sharing#gid=0` @@ -185,33 +185,33 @@ _0Ag7vwNTdThiNdDNNecDYclUsMzZ1R0JpbXdUaERMUVE_ Grâce à la [gem google drive](https://github.com/gimite/google-drive-ruby), nous pouvons lire le contenu de la Google Sheet relativement facilement : -{{< highlight ruby >}} +```ruby document_id = "0Ag7vwNTdThiNdDNNecDYclUsMzZ1R0JpbXdUaERMUVE" ws = session.spreadsheet_by_key(document_id).worksheets[0] puts ws[1,2] -{{< /highlight >}} +``` Ici, on va lire la cellule sur la 1ère ligne, 2ème colonne de la première page. Il est aussi possible de modifier le document, -{{< highlight ruby >}} +```ruby ws[1, 2] = “new value” ws.save -{{< /highlight >}} +``` de récupérer le nombre de colonnes ou de lignes, -{{< highlight ruby >}} +```ruby nbRows = ws.num_rows nbColumns = ws.num_cols -{{< /highlight >}} +``` ou encore de rafraichir le document pour récupérer les modifications effectuées par d’autres utilisateurs : -{{< highlight ruby >}} +```ruby ws.reload -{{< /highlight >}} +``` ## Conclusion @@ -221,3 +221,4 @@ On peut aller explorer l’API Google et essayer de piloter d’autres services De même, l'authentification avec un compte Google standard est identique. +```` diff --git a/site/content/posts/2015-03-31-integration-sogilis-bilan.md b/blog/_posts/2015-03-31-integration-sogilis-bilan.md similarity index 98% rename from site/content/posts/2015-03-31-integration-sogilis-bilan.md rename to blog/_posts/2015-03-31-integration-sogilis-bilan.md index f3051747a..07b7b7cc9 100644 --- a/site/content/posts/2015-03-31-integration-sogilis-bilan.md +++ b/blog/_posts/2015-03-31-integration-sogilis-bilan.md @@ -1,16 +1,16 @@ --- -title: "Mon intégration au sein de Sogilis : bilan après un mois" +title: 'Mon intégration au sein de Sogilis : bilan après un mois' author: Jérémy date: 2015-03-31T12:11:33+00:00 image: /img/2015-03-Jérémy-1.jpg categories: - - NOTRE VISION + - Notre vision tags: - intégration - témoignage - viedebureau - --- + Il y a quelques années, j’aspirais à mieux gérer mon temps et à prendre ma vie en main. En parcourant des blogs, des articles et des livres, j’ai été séduit par les valeurs et la conception de la vie de certains auteurs. À la fin de l’année 2014, juste après mon diplôme, je fais le choix de chercher l’entreprise qui correspond réellement à mes valeurs et à mes aspirations. Alors que mon entourage me soutient sans être absolument convaincu du réalisme de ma recherche, je pense l’avoir trouvée en janvier. Je ne connais rien des entreprises libérées mais les témoignages de ses salariés me font sauter le pas sans inquiétude. Voilà mes premières impressions après un mois ici. @@ -36,6 +36,6 @@ Rien que le fait d’avoir pu être autonome dès la première semaine est éton L’équipe de Sogilis est composée de personnes qui restent elles-mêmes. **Il n’y a pas de jugement de valeur sur la manière d’être de quelqu’un** ou de limites de bienséance. Je trouve ça vivant, je suis une personne qui aime bien lâcher prise. Quand j’entends un cri à l’autre bout du bureau ou quand il y a une bataille de Nerf [NDLR : des pistolets en jouet], cela me met de bonne humeur. **J’apprécie l’ambiance bon enfant, et c’est possible de travailler efficacement dans ces conditions.** En effet, on fait du travail de qualité, c’est ce que l’on attend de nous : cette atmosphère est motrice. ![](https://65.media.tumblr.com/9079124b3773c29628904594a9515f02/tumblr_inline_nlq30pkqgd1t2p7ex_500.jpg) -_Sortie buggy sur glace. Invité avant le premier jour de travail. Une super occasion de rencontrer ses futurs collègues. On se sent **très vite**_** intégré !_ +_Sortie buggy sur glace. Invité avant le premier jour de travail. Une super occasion de rencontrer ses futurs collègues. On se sent **très vite**_\*\* intégré !\_ J’ai eu une période de chômage parce que je voulais trouver l’Entreprise avec un grand E, du coup j’ai commencé un projet en parallèle avec un ami. **Je sais qu’il est possible d’entreprendre ici.** Si l’idée est en stand-by depuis mon embauche et la sienne, l’opportunité de continuer ce projet en étant à Sogilis reste ouverte. Aujourd’hui, je veux apprendre de la collaboration avec mes collègues tout en me considérant pertinent. Tant que j’aurai cet équilibre, je n’aurai aucune raison de partir, car le gain sera mutuel. **Aller au travail avec envie, c’est la plus belle des motivations. ** diff --git a/site/content/posts/2015-05-05-demystifying-git-concepts-to-understand.md b/blog/_posts/2015-05-05-demystifying-git-concepts-to-understand.md similarity index 91% rename from site/content/posts/2015-05-05-demystifying-git-concepts-to-understand.md rename to blog/_posts/2015-05-05-demystifying-git-concepts-to-understand.md index a9b8aeb0a..f0bd4a94b 100644 --- a/site/content/posts/2015-05-05-demystifying-git-concepts-to-understand.md +++ b/blog/_posts/2015-05-05-demystifying-git-concepts-to-understand.md @@ -1,16 +1,16 @@ --- -title: "Demystifying Git: 3 Concepts to Understand the Git Model" +title: 'Demystifying Git: 3 Concepts to Understand the Git Model' author: Simon Denier date: 2015-05-05T13:44:00+00:00 image: /img/2016-04-Sogilis-Christophe-Levet-Photographe-7898.jpg categories: - - DÉVELOPPEMENT + - Développement logiciel tags: - dev - en - git - --- + Git has a reputation for being a geeky tool with a steep learning curve, including a CLI with lots of options, and more concepts (staging, remote, push/pull, …) to start with than classic revision control tools. But Git has actually a simple conceptual model and does not attempt to hide it as implementation details. Learning this model and how Git operates on it will greatly speed up your apprenticeship of the tool. In other words, you will start to think like Git, and then finding or mixing commands will become much easier, with the occasional dive into the online help to get the right option. @@ -69,7 +69,7 @@ _Applying the B changeset onto commit F will only modify files bar and zorg. It Git, for example, see changes at the level of lines in text files. When you view a patch in Git (which is the textual representation of a changeset), you see something like that: -{{< highlight bash >}} +```bash diff --git a/app/scripts/Results.js b/app/scripts/Results.js index 98d5f30..ade6329 100644 --- a/app/scripts/Results.js @@ -90,19 +90,19 @@ index 98d5f30..ade6329 100644 + dataSource.status = 'error'; }); } -{{< /highlight >}} +``` To see this in action, just run `git diff HEAD~1 HEAD` in a repository. It will show you the changes between your latest commit and the previous one, directly in patch format. A patch file will register the following information: -* which file is impacted by the change (`app/scripts/Results.js`) -* where is the next change section in the file (`@@ -8,18 +8,20 @@`) -* lines removed by the change, prefixed by **-** -* lines added by the change, prefixed by **+** -* as a result, changed lines appear as removed (old version) then added (new version) -* _unchanged_ context lines before/after/between changed lines -* and so on for all sections and all files in the changeset +- which file is impacted by the change (`app/scripts/Results.js`) +- where is the next change section in the file (`@@ -8,18 +8,20 @@`) +- lines removed by the change, prefixed by **-** +- lines added by the change, prefixed by **+** +- as a result, changed lines appear as removed (old version) then added (new version) +- _unchanged_ context lines before/after/between changed lines +- and so on for all sections and all files in the changeset Git needs only to match the target files, line positions and textual context to make the changes described by the patch. In other words, it does not care about files from the snapshot which are untouched by changes. Even more, it does not care if other sections of the file have changed too. So it is easy for Git to copy changes from a commit on top of a different snapshot, provided context has not changed too much (otherwise, Git will detect and notify conflicts). @@ -114,9 +114,9 @@ _Changeset can update bar file, even if there is already a change in another pla Things start to become interesting when you know that Git can compute changesets (and patch files) on the fly between any two commits of your project. That means not only how to transform commit A into its child commit B, but also: -* the transformation from B to its grandchild D (in green below) -* the transformation from branch F to parallel branch D (in blue) -* or, why not, the reverse transformation from F to its parent E (in red, to cancel a change for example) +- the transformation from B to its grandchild D (in green below) +- the transformation from branch F to parallel branch D (in blue) +- or, why not, the reverse transformation from F to its parent E (in red, to cancel a change for example) _You can read the `git diff` form as `git diff [from] X [to] Y`._ @@ -128,3 +128,6 @@ In other words, Git is super effective in computing state transformations and ap This brief introduction to three concepts of revision tools is enough to understand the essentials of Git. In the [next article](https://blog.sogilis.com/posts/2015-05-12-demystifying-git-concepts/) we will put this knowledge in action to explain how some Git commands operate on your project. +``` + +``` diff --git a/site/content/posts/2015-05-12-demystifying-git-concepts.md b/blog/_posts/2015-05-12-demystifying-git-concepts.md similarity index 99% rename from site/content/posts/2015-05-12-demystifying-git-concepts.md rename to blog/_posts/2015-05-12-demystifying-git-concepts.md index d353074b1..fb15e1bda 100644 --- a/site/content/posts/2015-05-12-demystifying-git-concepts.md +++ b/blog/_posts/2015-05-12-demystifying-git-concepts.md @@ -1,18 +1,17 @@ --- -title: "Demystifying Git: 3 Concepts to Do Everything with Git" +title: 'Demystifying Git: 3 Concepts to Do Everything with Git' author: Simon Denier date: 2015-05-12T13:05:31+00:00 image: /img/2016-05-Sogilis-Christophe-Levet-Photographe-8824-e1462795824232.jpg categories: - - DÉVELOPPEMENT + - Développement logiciel tags: - dev - en - git - --- -**In the [previous article](https://blog.sogilis.com/posts/2015-05-05-demystifying-git-concepts-to-understand/), we saw three essential concepts of revision tools: snapshot, graph, and changeset. In the following, we can apply these concepts to understand how Git commands operate on your project. This will teach us how to think like Git.** +**In the [previous article](https://blog.sogilis.com/posts/2015-05-05-demystifying-git-concepts-to-understand/), we saw three essential concepts of revision tools: snapshot, graph, and changeset. In the following, we can apply these concepts to understand how Git commands operate on your project. This will teach us how to think like Git.** ## Updating the Working Copy (Switching between Branches, Pulling) diff --git a/site/content/posts/2015-07-16-victor-entrepreneuriat.md b/blog/_posts/2015-07-16-victor-entrepreneuriat.md similarity index 99% rename from site/content/posts/2015-07-16-victor-entrepreneuriat.md rename to blog/_posts/2015-07-16-victor-entrepreneuriat.md index 1bb72787d..f3626b5e1 100644 --- a/site/content/posts/2015-07-16-victor-entrepreneuriat.md +++ b/blog/_posts/2015-07-16-victor-entrepreneuriat.md @@ -4,13 +4,13 @@ author: Tiphaine date: 2015-07-16T13:01:42+00:00 image: /img/2015-07-Sogilis-Christophe-Levet-Photographe-7533.jpg categories: - - LA VIE À SOGILIS + - La vie à sogilis tags: - interview - Sogiteam - viedebureau - --- + **Depuis le 4 février 2013, on a pris l’habitude de retrouver Vicky tous les matins avec plaisir. En stage de fin d'études à l'Ensimag puis en CDI au sein de la Sogiteam durant deux ans, il s'envole aujourd’hui pour monter sa boîte. Victor, c'est 26 ans de vitamines : il est co-organisateur du [Startup Weekend Grenoble](https://fr-fr.facebook.com/StartupWeekendGrenoble/) et des [HumanTalks](https://humantalks.com/cities/grenoble), ces conférences proposées par les développeurs pour les développeurs tous les mois dans les villes de France. Et comme il a plusieurs cordes à son arc, quand il ne fait pas le poisson dans l’eau, il est capable de gravir le Mont-Blanc !** Le virus de l'entrepreneuriat l'a piqué lors de la première édition du Startup Weekend Grenoble. Depuis, il a enthousiasmé un bon nombre de clients, s'est creusé les méninges et a fortement contribué à la banane de l'équipe. Retour sur ces 2 bougies soufflées à Sogilis et mise en lumière de ses perspectives ! diff --git a/site/content/posts/2016-02-08-sogilis-vs-ssii.md b/blog/_posts/2016-02-08-sogilis-vs-ssii.md similarity index 99% rename from site/content/posts/2016-02-08-sogilis-vs-ssii.md rename to blog/_posts/2016-02-08-sogilis-vs-ssii.md index 49ffc52c7..1f5204400 100644 --- a/site/content/posts/2016-02-08-sogilis-vs-ssii.md +++ b/blog/_posts/2016-02-08-sogilis-vs-ssii.md @@ -1,16 +1,16 @@ --- -title: "SSII : Attends chérie… C’est pas c’que tu crois !" +title: 'SSII : Attends chérie… C’est pas c’que tu crois !' author: Jérémy et Corentin date: 2016-02-08T16:12:25+00:00 image: /img/2016-04-2.On-simplique.jpg categories: - - NOTRE VISION + - Notre vision tags: - amélioration continue - management - "modèle d'organisation" - --- + ## Ceci n'est pas une SSII / ESN / Société de service / Boîte de consulting... Nous n'aimons pas nous appeler SSII. Vous allez dire que c'est le cas de toutes les SSII, et vous avez raison. diff --git a/site/content/posts/2016-02-18-bookmarks-bla-bla.md b/blog/_posts/2016-02-18-bookmarks-bla-bla.md similarity index 99% rename from site/content/posts/2016-02-18-bookmarks-bla-bla.md rename to blog/_posts/2016-02-18-bookmarks-bla-bla.md index 367f08584..87c1d7a98 100644 --- a/site/content/posts/2016-02-18-bookmarks-bla-bla.md +++ b/blog/_posts/2016-02-18-bookmarks-bla-bla.md @@ -4,14 +4,14 @@ author: Yves Brissaud date: 2016-02-18T13:49:31+00:00 image: /img/2016-04-1.Formations.jpg categories: - - DÉVELOPPEMENT + - Développement logiciel tags: - clojure - go - javascript - veille - --- + Petit aperçu commenté de notre veille de ces derniers jours. Le principe n'est pas de vous fournir une suite de lien mais plutôt que ces liens racontent une histoire, ce qui nous intéresse ou nous interpelle. Mais tout de même, pour les plus pressés, la liste de l'ensemble des liens est dispo à la fin de l'article. ## Un peu de contenu @@ -48,15 +48,15 @@ David Heinemeier Hansson (créateur de Ruby on Rails, Basecamp, etc.) n'est quan Si vous avez déjà fait du javaScript (_What else?_), vous avez nécessairement été confronté à la problématique de _bind_ et au fait que _this_ n'a pas le même comportement que dans la plupart des langages habituels. Après pas mal de bricolages, on est arrivé à avoir une solution correcte en _ES5_ : -{{< highlight js >}} +````js $('.some-link').on('click', view.reset.bind(view)) -{{< /highlight >}} +``` Avec la méthode `bind` présente sur les objets on peut ainsi s'assurer que la méthode `reset` de `view` sera bien appelée sur l'objet `view` et non sur l'objet DOM derrière `$('.some-link')`. Cette méthode `bind` est quand même une grande avancée. Mais _ES7_ va encore plus loin (même si ce n'est pour le moment qu'une proposition) : -{{< highlight js >}} +```js $('.some-link').on('click', ::view.reset) -{{< /highlight >}} +``` L'introduction de l'opérateur `::` réalise justement la même chose que le `bind` précédent avec une plus grande lisibilité et de manière un peu moins verbeuse. Si vous voulez en savoir un peu plus sur cet opérateur (qui ne fait pas que le `bind`), je vous suggère d'aller lire [cet article](https://babeljs.io/blog/2015/05/14/function-bind). @@ -130,3 +130,4 @@ Et comme on [apprécie les Lego](https://twitter.com/_crev_/status/6437084268419 * [Lego Paper Plane Machine](https://www.youtube.com/watch?v=jU7dFrxvPKA) ![](https://www.gravatar.com/avatar/2405b32ff817cd55c9e5404e004b048b.png) +```` diff --git a/site/content/posts/2016-03-03-bookmarks-bla-bla-2.md b/blog/_posts/2016-03-03-bookmarks-bla-bla-2.md similarity index 98% rename from site/content/posts/2016-03-03-bookmarks-bla-bla-2.md rename to blog/_posts/2016-03-03-bookmarks-bla-bla-2.md index ce53897ac..cd46d3068 100644 --- a/site/content/posts/2016-03-03-bookmarks-bla-bla-2.md +++ b/blog/_posts/2016-03-03-bookmarks-bla-bla-2.md @@ -1,19 +1,18 @@ --- -title: "De tout, de rien, des bookmarks, du bla bla #2" +title: 'De tout, de rien, des bookmarks, du bla bla #2' author: Yves Brissaud date: 2016-03-03T10:40:50+00:00 image: /img/2016-04-2.Developpement-1.jpg categories: - - DÉVELOPPEMENT + - Développement logiciel tags: - elm - google - javascript - veille - --- -Voici un aperçu de notre veille de ces deux dernières semaines. Cet article fait suite [au précédent](https://blog.sogilis.com/posts/2016-02-18-bookmarks-bla-bla/) et que j'espère être le début d'une série régulière. +Voici un aperçu de notre veille de ces deux dernières semaines. Cet article fait suite [au précédent](https://blog.sogilis.com/posts/2016-02-18-bookmarks-bla-bla/) et que j'espère être le début d'une série régulière. Comme le nom l'indique, vous trouverez de tout et de rien, principalement autour du développement (langages, infrastructure) mais aussi design (CSS, SVG), méthodes (Story Map, _capabilities_). Plus qu'une suite de liens, vous trouverez ici une histoire. Cette histoire raconte, de manière subjective, les éléments qui nous ont conduit à s'intéresser à ces différents sujets. Et pour ceux qui s'intéressent plus aux liens qu'au voyage, l'ensemble des liens est présenté en liste à la fin de l'article. @@ -45,7 +44,7 @@ Il existe, depuis longtemps, de très nombreuses études, articles, blog post, s JavaScript continue encore et encore d'évoluer. Néanmoins, il existe souvent un décallage entre ce que le langage propose et ce qui est supporté par les navigateurs, un peu dans tous les sens. Quoi qu'il en soit vous serez ravis d'apprendre que de nouvelles [méthodes sur les objets sont arrivées dans Chrome](https://twitter.com/malyw/status/704972953029623808) : -{{< highlight js >}} +````js const obj = {0: 'a', 1: 'b', 2: 'c' }; Object.keys(obj); // ["0", "1", "2"] @@ -53,7 +52,7 @@ Object.values(obj); // ["a", "b", "c"] Object.entries(obj); // [["0", "a"], ["1", "b"], ["2", "c"]] -{{< /highlight >}} +``` > I believe JavaScript is usually not a good choice for server side applications. > @@ -76,7 +75,7 @@ Si vous souhaitez avoir un aperçu rapide de différents langages qui compilent Et pour aller encore un peu plus loin, voici une [version écrite en Elm](https://gist.github.com/eunomie/bd4d8d491d686580167b) que j'ai testé pour l'occasion. Il y a deux versions, à base de `sum`/`map` ou à base de `foldl`, je n'ai pas réussi à me décider sur la meilleure approche. Voici la version `foldl` : -{{< highlight elm >}} +```elm import List exposing (foldl) pow : Float -> Float -> Float @@ -92,7 +91,7 @@ fact : Float -> Float fact x = if x Float sine x = foldl (i sum -> sum + (pow -1 i) * (pow x (i * 2 + 1)) / (fact (i * 2 + 1))) 0 [0..9] -{{< /highlight >}} +``` ### CSS, SVG @@ -171,3 +170,4 @@ Et pour finir cet veille, voici un article basé sur des interviews de certains [1]: https://67.media.tumblr.com/4e8e610a765d515139c83dc932c21070/tumblr_inline_o3glitq8841sv6muh_500.png [2]: https://65.media.tumblr.com/d01433967c51343c087a92b8a1e1c8bb/tumblr_inline_o3glit0sNk1sv6muh_500.png [3]: https://66.media.tumblr.com/fc1aa83fbbcd44a53c57fb8aa83be904/tumblr_inline_o3glitBxNN1sv6muh_500.jpg +```` diff --git a/site/content/posts/2016-04-05-purejs-template-engine-without-markup.md b/blog/_posts/2016-04-05-purejs-template-engine-without-markup.md similarity index 87% rename from site/content/posts/2016-04-05-purejs-template-engine-without-markup.md rename to blog/_posts/2016-04-05-purejs-template-engine-without-markup.md index bd81399b0..cbfd3135d 100644 --- a/site/content/posts/2016-04-05-purejs-template-engine-without-markup.md +++ b/blog/_posts/2016-04-05-purejs-template-engine-without-markup.md @@ -1,15 +1,15 @@ --- -title: "pure.js: template engine without markup" +title: 'pure.js: template engine without markup' author: Shanti date: 2016-04-05T09:49:45+00:00 image: /img/2016-04-1.Produits.jpg categories: - - DÉVELOPPEMENT + - Développement logiciel tags: - pure.js - template - --- + **Ces derniers dojo, on a vu des langages vraiment sympa : Clojure avec OM et Elm. Par contre, il y a un petit truc qui me chiffonne : les templates.** Autant faire du template en Lisp, ça passe assez bien. C'est fait pour ça. Mais avec Elm, ce n'est vraiment pas joli (mon opinion seulement). D'un point de vue propreté, je serais plus à l'aise avec du markup séparé du code. Comme ce qu'on faisait en xHTML à l'époque où ça existait. Le mantra c'était : séparer la logique (JS) de la sémantique (html) de la présentation (CSS). Et j'aime toujours ce mantra. @@ -20,8 +20,8 @@ C'est du HTML sans même un attribut spécifique. Comment ça marche ? -* Soit la structure JSON est très proche du markup : pure.js va directement remplir les balises HTML avec le contenu du JSON. -* Soit la structure est un peu différente – comme c'est souvent le cas si le JSON n'est pas spécifique au markup : on a un JSON intermédiaire qui permet de dire à quel nœud HTML chaque objet JSON doit être mappé. Le mapping se fait avec des sélecteurs CSS. +- Soit la structure JSON est très proche du markup : pure.js va directement remplir les balises HTML avec le contenu du JSON. +- Soit la structure est un peu différente – comme c'est souvent le cas si le JSON n'est pas spécifique au markup : on a un JSON intermédiaire qui permet de dire à quel nœud HTML chaque objet JSON doit être mappé. Le mapping se fait avec des sélecteurs CSS. [Allez voir](https://beebole.com/pure/), il y a un exemple simple en haut pour comprendre comment ça marche. diff --git a/site/content/posts/2016-05-31-sogilis-histoire.md b/blog/_posts/2016-05-31-sogilis-histoire.md similarity index 99% rename from site/content/posts/2016-05-31-sogilis-histoire.md rename to blog/_posts/2016-05-31-sogilis-histoire.md index de4e912ec..0fce33424 100644 --- a/site/content/posts/2016-05-31-sogilis-histoire.md +++ b/blog/_posts/2016-05-31-sogilis-histoire.md @@ -1,17 +1,17 @@ --- -title: "Sogilis : notre histoire" +title: 'Sogilis : notre histoire' author: Myriam, avec l'aide de l'équipe date: 2016-05-31T14:36:55+00:00 image: /img/2016-04-0.Haut_page.png categories: - - NOTRE VISION + - Notre vision tags: - accompagnement - amélioration continue - développement - management - --- + Sogilis, aujourd’hui, c’est une trentaine de personnes. Ce sont des périodes intensives pour créer des logiciels de pointe, des kilos de croissants savourés, la contribution à des applis et des produits innovants, des fous-rires partagés… Sogilis, c’est l’histoire d’une société de service qui a décidé de casser les codes et de l’assumer haut et fort. Parce que proposer, innover, découvrir, partager, se tromper, c’est un quotidien qui nous motive tous. Retour sur 8 années passionnantes. ## La genèse de Sogilis : la mise en place d’une culture d’innovation @@ -54,7 +54,6 @@ La remise en question, le partage des bonnes pratiques, nous ont également amen Confrontés à des problématiques et ayant su s’adapter au regard de l’intelligence de l’équipe, nous sommes allés au-delà du développement logiciel. La capacité à innover est d’autant plus forte avec de jeunes sociétés, qui ont besoin d’être accompagnées de l’idée au marché. Alors que les porteurs de projets sont souvent victimes du syndrome du garage (développer un produit en allouant toutes ses ressources sans se confronter au marché, et être en décalage avec la réalité), et dans une entreprise où l’innovation est favorisée, nous avons décidé de proposer une offre d’accompagnement inédite. L’approche start-up étant une réalité vécue par notre expérience, nous commençons à accompagner [les porteurs de projet autour de leur business model](https://blog.sogilis.com/posts/2015-01-15-vision-business/) grâce à la connaissance de [Xavier](https://www.linkedin.com/in/xavier-spengler-9aa4423) et [Laurent](https://www.linkedin.com/in/laurent-mangue-885a6a29?authType=NAME_SEARCH&authToken=Upl-&locale=fr_FR&trk=tyah&trkInfo=clickedVertical%3Amynetwork%2CclickedEntityId%3A101168741%2CauthType%3ANAME_SEARCH%2Cidx%3A1-1-1%2CtarId%3A1459846582046%2Ctas%3Alaurent%20mang"). **Profiter du développement itératif que nous pratiquons pour tester, avoir un retour sur le marché et générer rapidement des revenus.** Du prototypage et du test marché, jusqu’aux logiciels sur mesure de qualité. Comme les porteurs de projet ont des difficultés à se financer, nous recrutons [Théo](https://www.linkedin.com/in/th%C3%A9o-schwartz-b7163358), un spécialiste de l’accompagnement sur les problématiques de financements publics, privés ou bancaires. Cerise sur le gâteau avec un renfort côté communication si nécessaire, avec [Myriam](https://www.linkedin.com/in/myriammenneteau?trk=hp-identity-name) à nos côtés. - ![](/img/2016-04-3.CS_BUSINESS_BasPage-300x200.jpg) ### Commercer autrement, ou comment renverser le modèle des sociétés de service @@ -111,10 +110,10 @@ Libérer la capacité d’entreprendre et créer des vocations suscite l’inté ![](/img/2016-05-Sogilis-Christophe-Levet-Photographe-8314-300x210.jpg) ## Nos perspectives : de la tour Eiffel au pays des kangourous - -Que retenir de Sogilis aujourd’hui ? Des projets, encore et toujours ! Des jeunes pousses à de grandes sociétés telles qu'une banque, nous travaillons toujours dans des secteurs variés. Notre leitmotiv, côté développement, [Shanti le résume très bien : créer des logiciels qui vont apporter à l’utilisateur final, enthousiasmer la personne qui va l’utiliser](https://www.youtube.com/watch?v=qaMI7SlFnoQ). Notre philosophie est toujours la même, quelle que soit l’utilisation des logiciels, dans un avion ou pour une appli mobile. -**Aujourd’hui 33 au sein de l’équipe, nous avons monté une filiale en Australie et à Lyon.** [Etienne](https://www.linkedin.com/profile/view?id=AAkAAAIOPGwBwM_awYquG7-SeKbFea84ED3gXBA&authType=NAME_SEARCH&authToken=lYiP&locale=fr_FR&trk=tyah&trkInfo=clickedVertical%3Amynetwork%2CclickedEntityId%3A34487404%2CauthType%3ANAME_SEARCH%2Cidx%3A1-1-1%2CtarId%3A1441636570402%2Ctas%3Aetienne) et [Laurie](https://www.linkedin.com/in/lguetat?authType=NAME_SEARCH&authToken=qBmU&locale=fr_FR&trk=tyah&trkInfo=clickedVertical%3Amynetwork%2CclickedEntityId%3A33028237%2CauthType%3ANAME_SEARCH%2Cidx%3A1-3-3%2CtarId%3A1459847612973%2Ctas%3Al), qui comptaient partir quelques années en Australie, continuent de travailler avec nous à Melbourne. [Xavier](https://www.linkedin.com/profile/view?id=AAkAAACZNxIB0yEK0H7p_BqdldUol7f1B0hid58&authType=NAME_SEARCH&authToken=NUYu&locale=fr_FR&trk=tyah&trkInfo=clickedVertical%3Amynetwork%2CclickedEntityId%3A10041106%2CauthType%3ANAME_SEARCH%2Cidx%3A1-1-1%2CtarId%3A1441636559052%2Ctas%3Axavier) crée une équipe autonome sur Lyon, également au travers d’une filiale. Bien entendu, toutes les entités partagent les valeurs, la vision et les croyances qui font notre différence. **On a encore quelques belles aventures en train de naître.** [Valentin](https://www.linkedin.com/profile/view?id=AAkAAAK3-E8B7noUnJ2d-n4snLm9KcWSMVNJL9c&authType=NAME_SEARCH&authToken=Hls-&locale=fr_FR&trk=tyah&trkInfo=clickedVertical%3Amynetwork%2CclickedEntityId%3A45611087%2CauthType%3ANAME_SEARCH%2Cidx%3A1-1-1%2CtarId%3A1441636582282%2Ctas%3Avalen) est à Paris en tant que business dev, afin de développer une activité produits issue de notre R&D. +Que retenir de Sogilis aujourd’hui ? Des projets, encore et toujours ! Des jeunes pousses à de grandes sociétés telles qu'une banque, nous travaillons toujours dans des secteurs variés. Notre leitmotiv, côté développement, [Shanti le résume très bien : créer des logiciels qui vont apporter à l’utilisateur final, enthousiasmer la personne qui va l’utiliser](https://www.youtube.com/watch?v=qaMI7SlFnoQ). Notre philosophie est toujours la même, quelle que soit l’utilisation des logiciels, dans un avion ou pour une appli mobile. + +**Aujourd’hui 33 au sein de l’équipe, nous avons monté une filiale en Australie et à Lyon.** [Etienne](https://www.linkedin.com/profile/view?id=AAkAAAIOPGwBwM_awYquG7-SeKbFea84ED3gXBA&authType=NAME_SEARCH&authToken=lYiP&locale=fr_FR&trk=tyah&trkInfo=clickedVertical%3Amynetwork%2CclickedEntityId%3A34487404%2CauthType%3ANAME_SEARCH%2Cidx%3A1-1-1%2CtarId%3A1441636570402%2Ctas%3Aetienne) et [Laurie](https://www.linkedin.com/in/lguetat?authType=NAME_SEARCH&authToken=qBmU&locale=fr_FR&trk=tyah&trkInfo=clickedVertical%3Amynetwork%2CclickedEntityId%3A33028237%2CauthType%3ANAME_SEARCH%2Cidx%3A1-3-3%2CtarId%3A1459847612973%2Ctas%3Al), qui comptaient partir quelques années en Australie, continuent de travailler avec nous à Melbourne. [Xavier](https://www.linkedin.com/profile/view?id=AAkAAACZNxIB0yEK0H7p_BqdldUol7f1B0hid58&authType=NAME_SEARCH&authToken=NUYu&locale=fr_FR&trk=tyah&trkInfo=clickedVertical%3Amynetwork%2CclickedEntityId%3A10041106%2CauthType%3ANAME_SEARCH%2Cidx%3A1-1-1%2CtarId%3A1441636559052%2Ctas%3Axavier) crée une équipe autonome sur Lyon, également au travers d’une filiale. Bien entendu, toutes les entités partagent les valeurs, la vision et les croyances qui font notre différence. **On a encore quelques belles aventures en train de naître.** [Valentin](https://www.linkedin.com/profile/view?id=AAkAAAK3-E8B7noUnJ2d-n4snLm9KcWSMVNJL9c&authType=NAME_SEARCH&authToken=Hls-&locale=fr_FR&trk=tyah&trkInfo=clickedVertical%3Amynetwork%2CclickedEntityId%3A45611087%2CauthType%3ANAME_SEARCH%2Cidx%3A1-1-1%2CtarId%3A1441636582282%2Ctas%3Avalen) est à Paris en tant que business dev, afin de développer une activité produits issue de notre R&D. ## Et Sogilis dans 10 ans ? diff --git a/site/content/posts/2016-06-03-journee-accompagnement-2906.md b/blog/_posts/2016-06-03-journee-accompagnement-2906.md similarity index 86% rename from site/content/posts/2016-06-03-journee-accompagnement-2906.md rename to blog/_posts/2016-06-03-journee-accompagnement-2906.md index c56f6f7c3..751dc50d1 100644 --- a/site/content/posts/2016-06-03-journee-accompagnement-2906.md +++ b/blog/_posts/2016-06-03-journee-accompagnement-2906.md @@ -4,15 +4,14 @@ author: Tiphaine date: 2016-06-03T09:00:28+00:00 image: /img/2015-02-storymap.jpg categories: - - NOTRE VISION + - Notre vision tags: - accompagnement - business - businessmodel - --- -**Alors que l'équipe de Sogilis Lyon travaille sur le drone sous-marin [iBubble](https://ibubble.camera/) et les lunettes connectées [Tikaway](https://www.tikaway.com/), elle propose aux porteurs de projets lyonnais une demi-journée d’accompagnement au design de leur Business Model.** +**Alors que l'équipe de Sogilis Lyon travaille sur le drone sous-marin [iBubble](https://ibubble.camera/) et les lunettes connectées [Tikaway](https://www.tikaway.com/), elle propose aux porteurs de projets lyonnais une demi-journée d’accompagnement au design de leur Business Model.** ![](/img/2016-06-20162906_Accompagnement_BMD-300x300.jpg) @@ -22,13 +21,13 @@ Après une courte présentation des projets, Xavier Spengler et Nicolas Comte, **Vous êtes :** -* porteur de projet(s) ou dirigeant d’une société -* disponible à Lyon le 29 juin de 14h à 17h +- porteur de projet(s) ou dirigeant d’une société +- disponible à Lyon le 29 juin de 14h à 17h **Vous souhaiteriez :** -* recevoir des conseils personnalisés afin d’accélérer votre accès aux premiers revenus et à l’adéquation produit/marché -* designer votre business model par la mise en place de tests itératifs +- recevoir des conseils personnalisés afin d’accélérer votre accès aux premiers revenus et à l’adéquation produit/marché +- designer votre business model par la mise en place de tests itératifs **Veuillez [remplir ce formulaire](https://docs.google.com/a/sogilis.com/forms/d/1wJPwCVqdvENFQGqgU6lJCcu7pl59uEGfvksMrLWiia8/edit) afin d’être sélectionné parmi les porteurs de projet(s).** diff --git a/site/content/posts/2016-06-07-partage-valeur-juste-egalitaire.md b/blog/_posts/2016-06-07-partage-valeur-juste-egalitaire.md similarity index 99% rename from site/content/posts/2016-06-07-partage-valeur-juste-egalitaire.md rename to blog/_posts/2016-06-07-partage-valeur-juste-egalitaire.md index 421911ab0..5440b26ab 100644 --- a/site/content/posts/2016-06-07-partage-valeur-juste-egalitaire.md +++ b/blog/_posts/2016-06-07-partage-valeur-juste-egalitaire.md @@ -1,15 +1,15 @@ --- -title: "Partage de la valeur : l’équilibre entre juste et égalitaire" +title: 'Partage de la valeur : l’équilibre entre juste et égalitaire' author: Christophe Baillon date: 2016-06-07T08:42:41+00:00 image: /img/2016-06-Moneymoneymoney.jpg categories: - - NOTRE VISION + - Notre vision tags: - amélioration continue - "modèle d'organisation" - --- + Le partage de la valeur est un sujet sensible dans une organisation, qui cristallise de nombreux débats. Il y a plusieurs approches dans la façon de partager la valeur créée par l’entreprise. Pour illustrer mon propos, je vais reprendre la définition que Jean-François Zobrist donne dans [le tome 2 du livre « La belle histoire de Favi », fiche 23](http://www.favi.com/wp-content/uploads/2015/10/23.pdf). ![](/img/2016-06-Belle-histoire-favi-201x300.jpg) @@ -49,6 +49,7 @@ La forme prise par la concrétisation de l’initiative peut être très diverse **Cette dynamique est passionnante, et génère des aventures innovantes toujours riches d’apprentissages, des opportunités extraordinaires, et une dynamique qui accentue l’enthousiasme des équipes.** Bien sûr, tout le monde ne souhaite pas entreprendre en même temps. Heureusement, car nous aurions un problème de bande passante. En revanche, cela responsabilise les équipes et montre que tout le monde peut librement prendre des actions pour atteindre sa mission le plus efficacement possible, la création d’une structure étant le cas extrême de la prise d’initiative. Un point important au sein de Sogilis, les salariés ne sont pas actionnaires. C’est rarement le cas des entreprises opales par ailleurs. Il y a principalement trois raisons qui motivent le fait d’être actionnaire : + - pouvoir participer aux décisions - toucher une partie du résultat par des dividendes - bénéficier d’une potentielle plus-value lors de la revente de la société @@ -56,6 +57,7 @@ Un point important au sein de Sogilis, les salariés ne sont pas actionnaires. C Le fait de rendre chaque salarié actionnaire est possible, mais compliqué, notamment en cas de départ. Sogilis a beau avoir un turnover très faible, il arrive qu’il y ait des départs, et il est juridiquement complexe de cadrer les conditions de sortie de l’actionnariat. Un pacte d’actionnaire pourrait imposer une revente des parts au nominal, mais si la valeur de l’entreprise augmente fortement - ce qui est inévitable dans une entreprise en croissance -, le droit du travail pourrait considérer la revente au nominal comme abusive. Ainsi, un rachat des parts du salarié sortant au prix fort pourrait mettre en danger la trésorerie de l’entreprise. Or, **chacune des 3 motivations d’être actionnaire est déjà adressée dans une entreprise opale (ou libérée)** : + - tout le monde participe aux décisions de façon intrinsèque via l’autogouvernance des équipes - les dispositifs d’intéressement, de participation, et de primes permettent de partager la valeur créée par l’entreprise de façon beaucoup plus souple - la société n’a pas pour vocation à être revendue, cela exclue donc une potentielle plus-value diff --git a/site/content/posts/2016-06-16-mixing-testing-logging-better-developer-experience.md b/blog/_posts/2016-06-16-mixing-testing-logging-better-developer-experience.md similarity index 93% rename from site/content/posts/2016-06-16-mixing-testing-logging-better-developer-experience.md rename to blog/_posts/2016-06-16-mixing-testing-logging-better-developer-experience.md index 8bc3b3e0c..d11840543 100644 --- a/site/content/posts/2016-06-16-mixing-testing-logging-better-developer-experience.md +++ b/blog/_posts/2016-06-16-mixing-testing-logging-better-developer-experience.md @@ -4,12 +4,12 @@ author: Simon date: 2016-06-16T15:06:54+00:00 image: /img/2016-06-Article-Simon.jpg categories: - - DÉVELOPPEMENT + - Développement logiciel tags: - développement - english - --- + People love a great article about a new programming paradigm/vision/motto which change the way you architect/develop/maintain your system. This is not one of those articles. **I am gonna talk about the stupid daily life of a programmer - and the problems you don't hear so much about because they seem so dull and obvious: getting the right feedback through logs and tests.** ## Getting the Right Feedback with Logging @@ -28,17 +28,17 @@ What to do with [logging](https://en.wikipedia.org/wiki/Logfile)? A log is a tra While developing my system, I might use log events to: -* validate its behavior: do I get a consistent trace, with events as expected? -* explore how it behaves when running an edge case -* diagnose what leads to an unexpected behavior +- validate its behavior: do I get a consistent trace, with events as expected? +- explore how it behaves when running an edge case +- diagnose what leads to an unexpected behavior But depending on the case, I want different levels of details about the behavior of my system. The default one is « info », since it traces all regular operations. It should be good enough when validating or exploring the system - with the occasional look at debug level when one wants to check finer details. Meanwhile, when debugging, I might run anywhere between the error level (because there is too much noise otherwise) and the debug or trace level (because I want maximum details about what led to this bad behavior). The important ability here is filtering events, either through a predefined level (all events below being discarded) or, even more interestingly, through a post-mortem filter which takes the full trace but only outputs the chosen events. -Let's take a look at bunyan again. I can configure a stream to output events at or above a level given by the `LOGLEVEL `environment variable. +Let's take a look at bunyan again. I can configure a stream to output events at or above a level given by the `LOGLEVEL `environment variable. -{{< highlight bash >}} +````bash bunyan.createLogger({ name: 'myApp', level: process.env.LOGLEVEL @@ -53,18 +53,18 @@ $ LOGLEVEL=info npm start 13:50:35.349Z ERROR mw: Can not get, Id unknown 10 (context=restApp) Error: Can not get, Id unknown 10 13:50:35.351Z WARN mw: GET /productions/10 -> Status 404 (context=restApp) -{{< /highlight >}} +``` Or I can use the bunyan CLI to filter log output on the command line. -{{< highlight bash >}} +```bash $ npm start | bunyan --level warn 13:49:47.171Z WARN mw: PrintingStopped - Item marked in error (context=printers) 13:50:35.349Z ERROR mw: Can not get, Id unknown 10 (context=restApp) Error: Can not get, Id unknown 10 13:50:35.351Z WARN mw: GET /productions/10 -> Status 404 (context=restApp) -{{< /highlight >}} +``` Pretty easy right? @@ -72,7 +72,7 @@ On larger systems, I also like to add some context data to each event - basicall Again, one can use the bunyan CLI to filter log output on the fly. -{{< highlight bash >}} +```bash const log = bunyanLogger.child({ context: 'restApp' }); @@ -83,18 +83,18 @@ $ npm start | bunyan --condition 'this.context == "restApp"' 13:50:35.349Z ERROR mw: Can not get, Id unknown 10 (context=restApp) Error: Can not get, Id unknown 10 13:50:35.351Z WARN mw: GET /productions/10 -> Status 404 (context=restApp) -{{< /highlight >}} +``` In any case, partitioning your log output to ease filtering is a good idea. I like the approach of [node debug](https://github.com/visionmedia/debug) (although, as its name implies, I would keep it for debugging purpose). -{{< highlight bash >}} +```bash const debug = require('debug')('restApp') debug('GET %s', route) $ DEBUG=restApp npm start restApp GET /printers -{{< /highlight >}} +``` That was rather down-to-earth. Notice I did not discuss where to put the log, how to rotate files etc. This is [not your app concern](http://12factor.net/logs): it should just dump events on standard output and let the infrastructure manages it. This, however, can have some impact on our next practice - testing. @@ -104,7 +104,7 @@ This is actually quite a hard topic. I will not discuss here the way to design y Sometimes I just want a basic feedback from my tests, something like a GREEN/RED status, because I am performing an health check, or refactoring my code. Let's try to run some tests naively, with logger default settings. -{{< highlight bash >}} +```bash $ npm test Started @@ -125,13 +125,13 @@ Started 52 specs, 0 failures Finished in 0.347 seconds -{{< /highlight >}} +``` For 52 specifications, I got 68 lines of output, mostly logs, which I don't really care about right now. Given my [tests are fast][3], I also would like to run them in watch mode. This would have the further inconvenience to flood my console with log output whenever I save a file and the tests run. Now let's say I can omit all log outputs. -{{< highlight bash >}} +```bash $ npm test Started @@ -140,13 +140,13 @@ Started 52 specs, 0 failures Finished in 0.26 seconds -{{< /highlight >}} +``` Nice and clear message: all is right on the front line. This is much better, and I can now use my watch mode to run tests continuously, without the noise of log output. Having a lean test report is also beneficial when you run into problems. When a test fails unexpectedly, I want to have the failure and the stack trace right in front fo my eyes, not having to scroll up pages and pages of logs. We could easily jump to the conclusion a null logger is the perfect tool to use in tests - one which redirects all output to`/dev/null`. -{{< highlight js >}} +```js const nullLogger = bunyan.createLogger({ name: 'nullLogger', streams: [{ @@ -154,7 +154,7 @@ const nullLogger = bunyan.createLogger({ }] }) // inject nullLogger in your modules while testing -{{< /highlight >}} +``` ## Customizing the Log Level for Tests @@ -164,28 +164,28 @@ However, there may be good reasons to not discard all outputs while running your The second reason is that you might have an error handler in your system: it could log the error then recover the bad case. With a null logger, the error would be swallowed and you will never get to see it in your test report. This can easily lead to head scratching or debugging nightmare as you have a failing test without the most basic cue that something went wrong somewhere in your system. -{{< highlight js >}} +```js try { // something may throw an Error } catch(e) { // log the error and recover log.error(e) // error redirected to /dev/null ! } -{{< /highlight >}} +``` This gives us two use cases for which we want to use log events in tests. The solution looks rather obvious. I should be able to customize the log level for my tests, so that I can get more details if need be. And by default, I only want errors to appear in my test log. This way, my test report is not disturbed by log events, unless something bad happens. -{{< highlight js >}} +```js const testLogger = bunyan.createLogger({ name: 'testLogger', level: (process.env.LOGLEVEL || 'error') }) // inject testLogger in your modules while testing -{{< /highlight >}} +``` In other words, _by default, log events should equate test failures_. -{{< highlight bash >}} +```bash $ npm test Started @@ -200,7 +200,7 @@ Failures: 52 specs, 1 failure Finished in 0.267 seconds npm ERR! Test failed. See above for more details. -{{< /highlight >}} +``` In the above example, my test fails on its assertion. But the root cause can easily be inferred from the error log screaming « Syntax Error ». @@ -208,7 +208,7 @@ In the above example, my test fails on its assertion. But the root cause can eas To cover our codebase, we could have a test case for error handling, checking that the error is recovered. Let's take the following sample and its accompanying test. -{{< highlight js >}} +```js function resilientParse(body) try { return JSON.parse(body) @@ -223,11 +223,11 @@ function resilientParse(body) resilientParse('not a JSON string') }).not.toThrowError(SyntaxError) }); -{{< /highlight >}} +``` Then running the test would produce an error output in the log. However, since the error is expected, having this output in our test report is not in line with _« log events should equate test failures »_. Why not silence the logger for this case? -{{< highlight js >}} +```js it('ignores non-json message', () => { expect(() => { let lvl = testLogger.level() @@ -236,7 +236,7 @@ it('ignores non-json message', () => { testLogger.level(lvl) }).not.toThrowError(SyntaxError) }) -{{< /highlight >}} +``` ## Final Guidelines @@ -250,3 +250,4 @@ What to keep of this stuff? Just follow those basic principles next time. **[Simon Denier][7]** [7]: https://twitter.com/simondenier +```` diff --git a/site/content/posts/2016-06-28-designing-hexo-mobile-app.md b/blog/_posts/2016-06-28-designing-hexo-mobile-app.md similarity index 97% rename from site/content/posts/2016-06-28-designing-hexo-mobile-app.md rename to blog/_posts/2016-06-28-designing-hexo-mobile-app.md index 1bcbc95fc..7c91ef9a2 100644 --- a/site/content/posts/2016-06-28-designing-hexo-mobile-app.md +++ b/blog/_posts/2016-06-28-designing-hexo-mobile-app.md @@ -1,15 +1,15 @@ --- -title: "Designing Hexo+ Mobile App (part #1)" +title: 'Designing Hexo+ Mobile App (part #1)' author: Laurie date: 2016-06-28T13:39:43+00:00 image: /img/2016-06-Hexo-NB.jpg categories: - - DÉVELOPPEMENT + - Développement logiciel tags: - design - mobile app - --- + ## A look into the design process for a connected product **In 2014, [Hexo+](https://hexoplus.com/) developed a working prototype and within six months of the initial idea had raised more than $1.3M on Kickstarter. Now more than 2600 Hexo+ intelligent drones and apps have just shipped all over the world.** @@ -40,7 +40,7 @@ We were looking for talented people with complementary skills, individuals who p ### Quick decisions guide the process -Once we had our team all set, we needed to make sure that we had access to the folks who can say ‘yes’ or ‘no’ to our design propositions. In a project as fast and furious as Hexo+, you’ve got to have someone inside the client’s organisation who makes decisions and makes them quickly. [The Product Owner (PO)](https://en.wikipedia.org/wiki/Scrum_(software_development)#Product_owner) role is crucial. +Once we had our team all set, we needed to make sure that we had access to the folks who can say ‘yes’ or ‘no’ to our design propositions. In a project as fast and furious as Hexo+, you’ve got to have someone inside the client’s organisation who makes decisions and makes them quickly. [The Product Owner (PO)]() role is crucial. [Eva](https://fr.linkedin.com/in/eva-pagneux-266a04b) was our ever-smiling and ever-demanding PO, the key interface between teams throughout Squadrone. She helped us understand the vision of what was to be built. At the end of each design sprint, she reviewed our propositions, narrowed down and made tough decisions, always keeping in mind big outcomes like user testing sessions, Hexo+ DIY kits or Hexo+ first shipping to our 2600+ Kickstarter backers all over the world. diff --git a/site/content/posts/2016-07-05-designing-hexo-mobile-app-design-sprints.md b/blog/_posts/2016-07-05-designing-hexo-mobile-app-design-sprints.md similarity index 97% rename from site/content/posts/2016-07-05-designing-hexo-mobile-app-design-sprints.md rename to blog/_posts/2016-07-05-designing-hexo-mobile-app-design-sprints.md index 1a166d1c9..41ba0667f 100644 --- a/site/content/posts/2016-07-05-designing-hexo-mobile-app-design-sprints.md +++ b/blog/_posts/2016-07-05-designing-hexo-mobile-app-design-sprints.md @@ -1,21 +1,20 @@ --- -title: "Designing Hexo+ Mobile App: Design Sprints (part #2)" +title: 'Designing Hexo+ Mobile App: Design Sprints (part #2)' author: Laurie date: 2016-07-05T15:06:09+00:00 image: /img/2016-06-Hexo-article-1.jpg categories: - - DÉVELOPPEMENT + - Développement logiciel tags: - design - mobile app - --- + ## How we ran iterations - -310+ sketches | 25+ Marvel prototypes | 2 public releases** -[Now that we set the project environment up](https://blog.sogilis.com/posts/2016-06-28-designing-hexo-mobile-app/), it was time to roll up our sleeves and start running design sprints. +310+ sketches | 25+ Marvel prototypes | 2 public releases\*\* +[Now that we set the project environment up](https://blog.sogilis.com/posts/2016-06-28-designing-hexo-mobile-app/), it was time to roll up our sleeves and start running design sprints. Working in fast-paced iterations, we ran a one-day workshop every week for taking a problem or feature from design through prototyping and testing. In this second part, we’ll provide a sneak peek into our design process for running a workshop and prototype Hexo+ user experience. @@ -62,7 +61,6 @@ The thing is, we realized timing the sequence was absolutely impossible. There w 3. With both dev and design teams we agreed on displaying an overview with a list of 3 items (Communication, Hardware, Position) showing a status (good / bad) and a detailed view for each group. - Another issue we had to face is the stabilisation of design approval. Stay tuned for the next episode, where we’ll talk about how feedback can be both extremely valuable and hard to manage. [**Laurie**][1] diff --git a/site/content/posts/2016-07-13-user-testing-decision-making.md b/blog/_posts/2016-07-13-user-testing-decision-making.md similarity index 98% rename from site/content/posts/2016-07-13-user-testing-decision-making.md rename to blog/_posts/2016-07-13-user-testing-decision-making.md index eaaff2666..c8dffca57 100644 --- a/site/content/posts/2016-07-13-user-testing-decision-making.md +++ b/blog/_posts/2016-07-13-user-testing-decision-making.md @@ -1,21 +1,20 @@ --- -title: "User testing and decision-making (part #3)" +title: 'User testing and decision-making (part #3)' author: Laurie date: 2016-07-13T10:19:55+00:00 image: /img/2016-07-CameraFollowBlack.jpg categories: - - DÉVELOPPEMENT + - Développement logiciel tags: - design - mobile app - --- + ## How we iterated on feedback [After running each workshop](https://blog.sogilis.com/posts/2016-07-05-designing-hexo-mobile-app-design-sprints/), we got a lot of ideas down on paper. It’s good to have ideas but you can’t test and build ‘em all. You have to make decisions. In the previous article, we talked about how we agreed on one or a few solutions and resisted the temptation to introduce any new ideas. Well, we did. Most of the time. But stabilising a design approval can be tricky. In the final two parts of the article series, we’ll talk about our decision-making process and how feedback can be both extremely valuable and hard to manage. - - ![testing](/img/2016-07-testing.gif) +![testing](/img/2016-07-testing.gif) ## Set the rules of the game diff --git a/site/content/posts/2016-07-26-hexo-stabilise-feedback.md b/blog/_posts/2016-07-26-hexo-stabilise-feedback.md similarity index 97% rename from site/content/posts/2016-07-26-hexo-stabilise-feedback.md rename to blog/_posts/2016-07-26-hexo-stabilise-feedback.md index f1dd2f094..bb1bf2c06 100644 --- a/site/content/posts/2016-07-26-hexo-stabilise-feedback.md +++ b/blog/_posts/2016-07-26-hexo-stabilise-feedback.md @@ -1,17 +1,16 @@ --- -title: "Hexo+: Stabilise feedback (part #4)" +title: 'Hexo+: Stabilise feedback (part #4)' author: Laurie date: 2016-07-26T09:30:32+00:00 image: /img/2016-07-xavproto_1-min-BW-min.jpg categories: - - DÉVELOPPEMENT + - Développement logiciel tags: - design - mobile app - --- -A few weeks after interviewing [one of our expert user](https://www.facebook.com/xvdelerue/), we questioned that [decision](https://blog.sogilis.com/posts/2016-07-13-user-testing-decision-making/) again. ‘The app is never really adapted to the field, there’s always a tree, some obstacle.’ When you are in the middle of nowhere with your drone, you probably want to understand what it’s going to do, what footage you’ll get, and where you stand in the landscape. You need to have a mental model of what’s gonna happen and create an imaginary link between you and your drone. This matter soon became our first concern. It addressed critical things such as anticipation, risk reduction and human error prevention. +A few weeks after interviewing [one of our expert user](https://www.facebook.com/xvdelerue/), we questioned that [decision](https://blog.sogilis.com/posts/2016-07-13-user-testing-decision-making/) again. ‘The app is never really adapted to the field, there’s always a tree, some obstacle.’ When you are in the middle of nowhere with your drone, you probably want to understand what it’s going to do, what footage you’ll get, and where you stand in the landscape. You need to have a mental model of what’s gonna happen and create an imaginary link between you and your drone. This matter soon became our first concern. It addressed critical things such as anticipation, risk reduction and human error prevention. ![xavproto_1-min](/img/2016-07-xavproto_1-min.jpg) diff --git a/site/content/posts/2016-07-28-on-recrute.md b/blog/_posts/2016-07-28-on-recrute.md similarity index 92% rename from site/content/posts/2016-07-28-on-recrute.md rename to blog/_posts/2016-07-28-on-recrute.md index fd25b5c26..91e71d2ed 100644 --- a/site/content/posts/2016-07-28-on-recrute.md +++ b/blog/_posts/2016-07-28-on-recrute.md @@ -4,11 +4,11 @@ author: Tiphaine date: 2016-07-28T10:37:41+00:00 image: /img/2016-07-Sogilis-Christophe-Levet-Photographe-7971.jpg categories: - - LA VIE À SOGILIS + - La vie à sogilis tags: - recrutement - --- + **Au sein de Sogilis, on a les oreilles ouvertes pour recruter des profils de passionnés passionnants à Grenoble et Lyon.** ![Recrutement4](/img/2016-07-Recrutement4.png) @@ -21,7 +21,6 @@ tags: **Si tu veux en savoir plus sur Sogilis, nous t’invitons à lire _[notre histoire sur le blog](https://blog.sogilis.com/posts/2016-05-31-sogilis-histoire/)_.** - **En pratique ? On fait de tout : du web, du back-end, de l’embarqué, du mobile.** À notre actif, nous avons développé un logiciel embarqué critique sur un gros avion de ligne, le soft embarqué du drone de loisir _[Hexo+](https://hexoplus.com/)_, des applications mobiles Android/iOS, une solution d’implantologie dentaire, un logiciel pour gérer un réseau d’imprimantes 3D. @@ -34,12 +33,11 @@ La diversité des projets implique des technos variées et qu’on adapte nos m **Vous avez envie de participer à l’aventure ?** -* Envoyez-nous votre CV. -* Présentez-nous vos motivations. -* Montrez-nous ce dont vous êtes fier(e) (ou pas !). +- Envoyez-nous votre CV. +- Présentez-nous vos motivations. +- Montrez-nous ce dont vous êtes fier(e) (ou pas !). -- [lyon.job@sogilis.com](mailto:lyon@sogilis.com) -- [grenoble.job@sogilis.com](mailto:grenoble.job@sogilis.com) +* [lyon.job@sogilis.com](mailto:lyon@sogilis.com) +* [grenoble.job@sogilis.com](mailto:grenoble.job@sogilis.com) [1]: https://blog.sogilis.com/posts/2016-02-08-sogilis-vs-ssii/ - diff --git a/site/content/posts/2016-08-16-objets-insolites-sogilis-1.md b/blog/_posts/2016-08-16-objets-insolites-sogilis-1.md similarity index 97% rename from site/content/posts/2016-08-16-objets-insolites-sogilis-1.md rename to blog/_posts/2016-08-16-objets-insolites-sogilis-1.md index c8498fcfc..67d412b8a 100644 --- a/site/content/posts/2016-08-16-objets-insolites-sogilis-1.md +++ b/blog/_posts/2016-08-16-objets-insolites-sogilis-1.md @@ -1,15 +1,15 @@ --- -title: "Les objets insolites au sein de Sogilis #1" +title: 'Les objets insolites au sein de Sogilis #1' author: Sogilis date: 2016-08-16T13:51:08+00:00 image: /img/2015-09-SogiPeluche.jpg categories: - - LA VIE À SOGILIS + - La vie à sogilis tags: - Fun - viedebureau - --- + **Minigolf, hamac, drones, figurines… et s’ils étaient réunis dans un bureau ? En nous appropriant notre espace de travail, nous avons laissé place à un certain nombre d’objets plus ou moins étonnants. Regardez plutôt !** ## 1. Les legos @@ -62,7 +62,7 @@ En temps normal, on ne la remarque pas. Elle sait rester discrète, cachée derr ![](https://66.media.tumblr.com/efc722d6121ef4cb95b3336b6b4c03c5/tumblr_inline_nv6oxaxGdU1t2p7ex_540.jpg) -## 9. ****La coupe SogiPong +## 9. \*\*\*\*La coupe SogiPong Chaque année à Sogilis, un tournoi de ping-pong est organisé par MousS’ qui fait tout ce qui est en son pouvoir pour nous satisfaire. Une coupe est offerte au meilleur pongiste. Comme Valentin règne sans partage sur le tournoi depuis sa création, il est désormais appelé SogiPong - Valentin Brossard. (_Enfin, ça c'était avant. JB a pris la relève...)_ diff --git a/site/content/posts/2016-08-23-objets-insolites-sogilis-2.md b/blog/_posts/2016-08-23-objets-insolites-sogilis-2.md similarity index 98% rename from site/content/posts/2016-08-23-objets-insolites-sogilis-2.md rename to blog/_posts/2016-08-23-objets-insolites-sogilis-2.md index 9a01eaca5..83bbb424e 100644 --- a/site/content/posts/2016-08-23-objets-insolites-sogilis-2.md +++ b/blog/_posts/2016-08-23-objets-insolites-sogilis-2.md @@ -1,17 +1,16 @@ --- -title: "Les objets insolites au sein de Sogilis #2" +title: 'Les objets insolites au sein de Sogilis #2' author: Sogilis date: 2016-08-23T08:00:15+00:00 image: /img/2015-09-Basket.jpg categories: - - LA VIE À SOGILIS + - La vie à sogilis tags: - Fun - viedebureau - --- -Un piano, une Wii, un plan de domination du monde… voici les objets qui nous entourent, parmi tant d’autres ! [Suite à une première liste](https://blog.sogilis.com/posts/2016-08-16-objets-insolites-sogilis-1/), on continue sur notre lancée. +Un piano, une Wii, un plan de domination du monde… voici les objets qui nous entourent, parmi tant d’autres ! [Suite à une première liste](https://blog.sogilis.com/posts/2016-08-16-objets-insolites-sogilis-1/), on continue sur notre lancée. ## 1. Le pomodoro diff --git a/site/content/posts/2016-08-30-sogilis-entreprise-pas-comme-autres.md b/blog/_posts/2016-08-30-sogilis-entreprise-pas-comme-autres.md similarity index 79% rename from site/content/posts/2016-08-30-sogilis-entreprise-pas-comme-autres.md rename to blog/_posts/2016-08-30-sogilis-entreprise-pas-comme-autres.md index 41e3d1f2d..4a3782522 100644 --- a/site/content/posts/2016-08-30-sogilis-entreprise-pas-comme-autres.md +++ b/blog/_posts/2016-08-30-sogilis-entreprise-pas-comme-autres.md @@ -4,28 +4,27 @@ author: Luc date: 2016-08-30T12:00:26+00:00 image: /img/2015-11-Luc.jpg categories: - - LA VIE À SOGILIS + - La vie à sogilis tags: - entrepriselibérée - témoignage - --- + Suite à mon départ de Sogilis en mai 2014, je n’ai pas cessé de m’intéresser aux entreprises libérées. Étant en veille active, je m'intéresse à plusieurs groupes sur le sujet. Il y a quelques jours encore, j’ai visualisé le [TEDx d’Alexandre Gérard sur la transformation de Chronoflex](https://youtu.be/VebUucpwAZc) [sa keynote à (Agile Grenoble](http://agile-grenoble.org/) 2014 m'avait déjà interpellé). Les valeurs portées par ces entreprises, notamment donner la confiance d’office, me paraissent essentielles et m’ont amené à repostuler à Sogilis. Je profite de ce retour pour faire une analyse du chemin parcouru ces dernières années. J’ai un peu eu la bougeotte et tout cela s’explique ! Après 10 ans passés chez un éditeur de logiciel où j’ai beaucoup appris sur la communication, l’agilité, le TDD, le BDD etc., j’ai souhaité découvrir d’autres projets, des nouvelles pratiques. J’avais besoin de changement, de nouveaux challenges, de nouveaux axes d’apprentissage. En fait, c’est assez normal : le temps où on entre dans une entreprise sans jamais en changer est révolu. En tout cas, c’est le conditionnement que nous recevons dès le plus jeune âge. J’avais donc postulé et intégré Sogilis pour son fonctionnement différent et la nature passionnée des collaborateurs. D’ailleurs, 4 mois après mon intégration, j’avais proposé un retour [ici](https://blog.sogilis.com/posts/2014-01-29-histoire-4-premiers-mois-sogilis/). Mon premier projet fut passionnant. En effet, [nous avons créé en équipe un logiciel d’implantologie dentaire pour 3DKreaForm](https://www.youtube.com/watch?v=N92L7xPVaDs) ; les défis fonctionnels et techniques étaient grands. **Nous avons énormément collaboré avec nos clients pour arriver à de chouettes résultats : valeur ajoutée importante, enthousiasme, budget contenu, plaisir…** Bref, que du bonheur. - ![](https://66.media.tumblr.com/55920c93c238dedf3af460cb47da0998/tumblr_inline_ny06riWQHu1t2p7ex_540.jpg) -J’ai également fait mes premiers pas en tant qu’agent de changement vers l'agilité à l’extérieur. [ScrumMaster](https://fr.wikipedia.org/wiki/Scrum_(m%C3%A9thode)) depuis plusieurs années en interne dans mon entreprise précédente, j’avais l’envie, mais surtout le trac, de me lancer. Étant d’un naturel réservé et pas toujours sûr de moi, [Laurent](https://www.linkedin.com/profile/view?id=AAkAAAYHtmUB62g-heow8wc1-hMMCmb_arygNkc&authType=NAME_SEARCH&authToken=Upl-&locale=fr_FR&trk=tyah&trkInfo=clickedVertical%3Amynetwork%2CclickedEntityId%3A101168741%2CauthType%3ANAME_SEARCH%2Cidx%3A1-1-1%2CtarId%3A1446825467252%2Ctas%3Alaurent%20mangue) m’a coaché (il a cette facilité naturelle), donné confiance et permis de me lancer plus sereinement. J’ai ressenti cette première expérience d’accompagnement comme un vrai plaisir. **Évangéliser une équipe sur les pratiques agiles en partant de zéro m’a fait comprendre que le changement prend du temps.** J’ai appris à être patient. L’intervention à temps partiel et la base de code très ancienne ont été les aspects les plus difficiles du projet. +J’ai également fait mes premiers pas en tant qu’agent de changement vers l'agilité à l’extérieur. [ScrumMaster]() depuis plusieurs années en interne dans mon entreprise précédente, j’avais l’envie, mais surtout le trac, de me lancer. Étant d’un naturel réservé et pas toujours sûr de moi, [Laurent](https://www.linkedin.com/profile/view?id=AAkAAAYHtmUB62g-heow8wc1-hMMCmb_arygNkc&authType=NAME_SEARCH&authToken=Upl-&locale=fr_FR&trk=tyah&trkInfo=clickedVertical%3Amynetwork%2CclickedEntityId%3A101168741%2CauthType%3ANAME_SEARCH%2Cidx%3A1-1-1%2CtarId%3A1446825467252%2Ctas%3Alaurent%20mangue) m’a coaché (il a cette facilité naturelle), donné confiance et permis de me lancer plus sereinement. J’ai ressenti cette première expérience d’accompagnement comme un vrai plaisir. **Évangéliser une équipe sur les pratiques agiles en partant de zéro m’a fait comprendre que le changement prend du temps.** J’ai appris à être patient. L’intervention à temps partiel et la base de code très ancienne ont été les aspects les plus difficiles du projet. Cela m’a beaucoup apporté, j’ai compris que j’étais compétent sur le sujet c’est important pour une personne (travaillomane). Puis, j’ai décidé d’aller encore plus loin et d’accepter de mener une transition vers l’agilité d’une entreprise de 30 personnes en quittant Sogilis. Relever ce challenge me permettrait de valider ma capacité à mettre en place les pratiques agiles et son état d’esprit. Ces ailes, c’est bien Sogilis qui me les a fait pousser. Je ne m’en suis pas vraiment rendu compte à ce moment-là. **La clef de tout, c’est la confiance : les collaborateurs remis au centre du jeu.** Évidemment, comme dans toute société, il y a des choses à améliorer et des contraintes. Mais l’autonomie, la liberté de proposer et d’entreprendre, l’écoute active, c’est plutôt rare. -J’ai donc débuté cette migration vers l’agilité dans cette nouvelle société dans un contexte très compliqué, autant au niveau de la communication que du management. Le [command & control](https://en.wikipedia.org/wiki/Command_and_control_(management)) était là, bien ancré. La situation m’a beaucoup surprise les 2 ou 3 premiers jours : la communication était très mal en point dans l’entreprise et les marges de manoeuvre semblaient très étroites. Par bonheur, j’avais toujours mes super ailes. +J’ai donc débuté cette migration vers l’agilité dans cette nouvelle société dans un contexte très compliqué, autant au niveau de la communication que du management. Le [command & control]() était là, bien ancré. La situation m’a beaucoup surprise les 2 ou 3 premiers jours : la communication était très mal en point dans l’entreprise et les marges de manoeuvre semblaient très étroites. Par bonheur, j’avais toujours mes super ailes. J’ai donc entrepris et travaillé très dur pour changer la culture d’entreprise qui était à bout de souffle, ne permettant pas de grandir et de mener à bien les nouveaux projets. Ce travail a été compliqué car très exposé. En effet, j’ai dû intervenir à peu près partout dans l’entreprise, du process RH à la gestion des prestataires, en passant par la stratégie produit qui devait devenir itérative, et enfin l’accompagnement des équipes projets. J’ai du faire énormément de pédagogie envers la direction et les équipes, en adaptant le discours à chaque situation. J’ai rapidement compris que la passion et l’envie seraient insuffisantes pour travailler dans ce contexte sur le long terme. diff --git a/site/content/posts/2016-09-06-srp-single-responsibility-principle-code.md b/blog/_posts/2016-09-06-srp-single-responsibility-principle-code.md similarity index 97% rename from site/content/posts/2016-09-06-srp-single-responsibility-principle-code.md rename to blog/_posts/2016-09-06-srp-single-responsibility-principle-code.md index 75ca040a0..703a3073b 100644 --- a/site/content/posts/2016-09-06-srp-single-responsibility-principle-code.md +++ b/blog/_posts/2016-09-06-srp-single-responsibility-principle-code.md @@ -4,13 +4,13 @@ author: Jean-Baptiste date: 2016-09-06T07:00:21+00:00 image: /img/2016-02-Sogilis-Christophe-Levet-Photographe-7803.jpg categories: - - DÉVELOPPEMENT + - Développement logiciel tags: - développement - SRP - --- -Le principe de responsabilité unique (**Single Responsibility Principle** ou SRP) fait partie d'un ensemble de 5 principes de la programmation orientée objet : [SOLID](https://fr.wikipedia.org/wiki/SOLID_(informatique)). + +Le principe de responsabilité unique (**Single Responsibility Principle** ou SRP) fait partie d'un ensemble de 5 principes de la programmation orientée objet : [SOLID](). - **S** : Single Responsibility Principle - **O** : Open/Closed Principle @@ -40,7 +40,7 @@ Encore une fois, naïvement, on peut se dire que c'est une fonctionnalité, une En appliquant cela sur une base de code, on arriverait alors à des classes minimalistes qui ne contiendraient qu'une seule méthode d'une seule ligne... **absurde**. -{{< highlight ruby >}} +````ruby public int compareArticlePriceToAverage(Article article, Collection
allArticles) {       return compare(article.getPrice(), computePriceAverage(allArticles)); } @@ -67,7 +67,7 @@ public double divide(double numerator, double denominator) { public int compare(double amount1, double amount2) {       return Double.valueOf(amount1).compareTo(amount2); } -{{< /highlight >}} +``` Il y a donc quelque chose qui cloche avec cette interprétation naïve. @@ -94,13 +94,13 @@ Ok, mais n'importe quelle raison est valable ? Comment peut-on connaître toutes Robert nous aide avec un exemple concret : -{{< highlight ruby >}} +```ruby public interface Modem {       public void Dial(String pno);       public void Hangup();       public void Send(char c);       public char Recv(); -}{{< /highlight >}} +}``` Il nous explique que les classes qui implémentent cette interface ont alors 2 responsabilités (la gestion de la connexion et la communication), mais qu'elles ne doivent pas nécessairement être scindées : tout dépend de la manière dont l'application évolue ! Si les évolutions ne porterons que sur la gestion de la connexion, alors oui, pour minimiser la rigidité, éviter de toucher à la partie communication lorsqu'on modifie la gestion de la connexion, etc., il est préférable de dissocier ces 2 responsabilités. En revanche, si les futures évolutions portent sur ces 2 aspects en même temps, alors cette séparation n'est pas nécessaire. Elle est même déconseillée pour éviter de compliquer l'architecture de manière inutile. @@ -116,7 +116,7 @@ Robert nous donne quelques exemples dans son article : - Une violation courante du SRP est l'accumulation de règles métiers et de gestion de persistance au sein d'une même classe : - {{< highlight ruby >}} + ```ruby public class Book {       public void save() {              // ... @@ -126,7 +126,7 @@ Robert nous donne quelques exemples dans son article :              // ...      } } - {{< /highlight >}} + ``` Robert est clair sur ce point : **c'est presque toujours à** **éviter** puisque les évolutions de ces 2 responsabilités ont des fréquences et des raisons différentes de changer. @@ -194,3 +194,4 @@ Voici d’autres méthodes ou pratiques, complémentaires ou non, qui donnent d - [DDD](https://en.wikipedia.org/wiki/Domain-driven_design) [Jean-Baptiste](https://fr.linkedin.com/in/jean-baptiste-mille-0383b81/fr) +```` diff --git a/site/content/posts/2016-09-27-mise-place-livret-accueil.md b/blog/_posts/2016-09-27-mise-place-livret-accueil.md similarity index 99% rename from site/content/posts/2016-09-27-mise-place-livret-accueil.md rename to blog/_posts/2016-09-27-mise-place-livret-accueil.md index bc33e901d..e68c9efe2 100644 --- a/site/content/posts/2016-09-27-mise-place-livret-accueil.md +++ b/blog/_posts/2016-09-27-mise-place-livret-accueil.md @@ -4,12 +4,12 @@ author: Myriam date: 2016-09-27T07:00:22+00:00 image: /img/2015-12-Sogilis-Christophe-Levet-Photographe-7811.jpg categories: - - LA VIE À SOGILIS + - La vie à sogilis tags: - intégration - vie interne - --- + **Lors d’une rétrospective mensuelle, nous avons eu l’idée de créer un livret d’accueil. Le problème rencontré : au moment de l’intégration, certains d’entre nous avaient la sensation de manquer d’informations. Si, à chaque nouvel arrivant dans l’équipe, une personne se propose pour l’accompagner, le livret d’accueil est apparu complémentaire. Décryptage.** ![Livret](/img/2015-12-Livret.jpg) @@ -52,7 +52,6 @@ Finalement, **être précis, drôle et informatif à la fois**. - les rendez-vous internes : _[rétrospective mensuelle](https://blog.sogilis.com/posts/2014-04-10-retrospective-mensuelle/)_, entretien individuel, update financier, Sogiday, etc. - - l’organisation : télétravail, mutuelle, etc. - le processus de recrutement : ici, c’est l’équipe qui recrute @@ -89,5 +88,4 @@ _[Myriam][1]_ - [Victor se lance dans l'entrepreneuriat](https://blog.sogilis.com/posts/2015-07-16-victor-entrepreneuriat/) - [1]: https://fr.linkedin.com/in/myriammenneteau diff --git a/site/content/posts/2016-10-25-compression-reconstruction-calculs-images-3d.md b/blog/_posts/2016-10-25-compression-reconstruction-calculs-images-3d.md similarity index 99% rename from site/content/posts/2016-10-25-compression-reconstruction-calculs-images-3d.md rename to blog/_posts/2016-10-25-compression-reconstruction-calculs-images-3d.md index d47753790..1b74762cc 100644 --- a/site/content/posts/2016-10-25-compression-reconstruction-calculs-images-3d.md +++ b/blog/_posts/2016-10-25-compression-reconstruction-calculs-images-3d.md @@ -4,11 +4,11 @@ author: Alexandre Dumont date: 2016-10-25T08:00:22+00:00 image: /img/2015-03-Sogilis-Christophe-Levet-Photographe-7461.jpg categories: - - DÉVELOPPEMENT + - Développement logiciel tags: - développement - --- + Ce billet présente l'algorithme PCA (Principal Component Analysis, ou encore analyse en composantes principales). Il s’agit d’une technique mathématique qui identifie et explique la corrélation d'un ensemble de données discrètes. Cet article pose les bases de cet algorithme et détaille une de ses applications pour la reconstruction d’image. ## Le PCA : une méthode, plein d'applications ! diff --git a/site/content/posts/2016-12-13-sogimood-appli-sante-projets.md b/blog/_posts/2016-12-13-sogimood-appli-sante-projets.md similarity index 91% rename from site/content/posts/2016-12-13-sogimood-appli-sante-projets.md rename to blog/_posts/2016-12-13-sogimood-appli-sante-projets.md index 6c856104c..f097d0659 100644 --- a/site/content/posts/2016-12-13-sogimood-appli-sante-projets.md +++ b/blog/_posts/2016-12-13-sogimood-appli-sante-projets.md @@ -4,15 +4,14 @@ author: Marien Fressinaud date: 2016-12-13T10:15:45+00:00 image: /img/2016-05-Sogilis-Christophe-Levet-Photographe-8218.jpg categories: - - DÉVELOPPEMENT + - Développement logiciel tags: - agile - amélioration continue - développement - --- -{{% disclaimer %}} + Nous publions aujourd’hui, sous licence libre, une application utilisée en interne pour surveiller notre activité : SogiMood. Explications sur l’outil. @@ -38,9 +37,9 @@ _La 0.6 qui devient aujourd'hui la 1.0._ Les fonctionnalités implémentées sont : -* renseigner les informations essentielles d'un projet : nom, description, date de début et fin de contrat et date de fin réestimée ; -* visualiser la période sur laquelle les projets s'étendent (sur une année au maximum) en mettant en évidence le retard constaté ; -* indiquer chaque semaine l'enthousiasme du client, le moral des équipes et la santé financière pour chaque projet. De plus, il est possible d'ajouter des notes et des jalons. +- renseigner les informations essentielles d'un projet : nom, description, date de début et fin de contrat et date de fin réestimée ; +- visualiser la période sur laquelle les projets s'étendent (sur une année au maximum) en mettant en évidence le retard constaté ; +- indiquer chaque semaine l'enthousiasme du client, le moral des équipes et la santé financière pour chaque projet. De plus, il est possible d'ajouter des notes et des jalons. La santé se mesure ainsi sur les trois objectifs que nous devons atteindre en tant que salarié : être rentable, enthousiasmer le client et s'éclater au boulot. Ces trois variables agrégées donnent un indicateur global pour la semaine. @@ -50,9 +49,9 @@ Dernièrement, Christophe nous a présenté le principe de sollicitation d'avis Comme SogiMood est avant tout un projet personnel développé pour m’amuser (bien qu'utilisé au sein de Sogilis), j'étais parti du principe que le choix de la licence n'intéresserait pas forcément les foules. Développant moi-même [quelques logiciels libres](https://github.com/marienfressinaud) sur mon temps libre, je partais sur une licence MIT… lorsqu'on me suggéra une licence Apache vis-à-vis des brevets… puis une licence type GPL car non-permissive. D'un choix unilatéral, on est donc passé à une sollicitation d'avis. J'ai donc pris le temps d'étudier le pour et le contre et le choix s'est révélé plus compliqué que prévu. Les licences considérées étaient les suivantes : -* MIT (licence permissive) : les droits sont larges et les contraintes très faibles. Elle a l'avantage d'être l'une des plus connues et les gens n'ont généralement pas besoin de se poser de questions quant à l'usage qu'ils peuvent faire du code ; -* Apache (licence permissive) : similaire à la MIT dans les faits, elle est toutefois conseillée par la FSF pour sa clause sur les brevets ; -* AGPL (licence non-permissive) : plus contraignante que les deux précédentes car elle oblige à redistribuer les modifications faites au logiciel sous licence libre, y compris si l’accès au logiciel est fait via un serveur (ce qui fait sa différence avec la licence GPL). +- MIT (licence permissive) : les droits sont larges et les contraintes très faibles. Elle a l'avantage d'être l'une des plus connues et les gens n'ont généralement pas besoin de se poser de questions quant à l'usage qu'ils peuvent faire du code ; +- Apache (licence permissive) : similaire à la MIT dans les faits, elle est toutefois conseillée par la FSF pour sa clause sur les brevets ; +- AGPL (licence non-permissive) : plus contraignante que les deux précédentes car elle oblige à redistribuer les modifications faites au logiciel sous licence libre, y compris si l’accès au logiciel est fait via un serveur (ce qui fait sa différence avec la licence GPL). Je suis toutefois resté sur le choix de la MIT qui est, selon moi, parfaitement adaptée à une lib ou une petite application qui n'a pas vocation à grossir. Il y a peu de chances que nous soyons confronté à des problèmes de brevet et je trouve que l'AGPL est moins « accueillante » vis-à-vis des contributions. J'utiliserais par contre volontiers cette dernière dans un contexte de société éditrice d'un logiciel open source. En revanche, SogiMood est un outil simple utilisé en interne et qui n'a pas la vocation de nous nourrir. C’est pourquoi j’ai préféré le libérer autant que possible ! @@ -66,7 +65,7 @@ Si on ne prévoit pas de faire évoluer le logiciel, il en est peut-être autrem Voici quelques liens qui peuvent être utiles : -* [Le dépôt de code](https://github.com/sogilis/SogiMood) -* [Le bugtracker](https://github.com/sogilis/SogiMood/issues) +- [Le dépôt de code](https://github.com/sogilis/SogiMood) +- [Le bugtracker](https://github.com/sogilis/SogiMood/issues) [Marien Fressinaud](https://twitter.com/berumuron) diff --git a/site/content/posts/2017-01-24-sonarqube-android-projects.md b/blog/_posts/2017-01-24-sonarqube-android-projects.md similarity index 92% rename from site/content/posts/2017-01-24-sonarqube-android-projects.md rename to blog/_posts/2017-01-24-sonarqube-android-projects.md index f4b7d38b3..ff98149cf 100644 --- a/site/content/posts/2017-01-24-sonarqube-android-projects.md +++ b/blog/_posts/2017-01-24-sonarqube-android-projects.md @@ -4,7 +4,7 @@ author: Jean-Baptiste date: 2017-01-24T09:26:44+00:00 image: /img/2016-04-2.Notre-vision.jpg categories: - - DÉVELOPPEMENT + - Développement logiciel tags: - Android - Couverture @@ -19,8 +19,8 @@ tags: - Qualité - Quality - SonarQube - --- + # How to monitor code quality of your Android application with SonarQube ? [SonarQube.org](http://www.sonarqube.org) is a powerful tool to monitor and analyze code quality, and especially technical debt on Java projects (and more). As Android projects are based on Java sources, it is possible to analyze such projects with SonarQube. But if you already tried this, you probably noticed how difficult it is to find right and complete documentation. @@ -43,7 +43,7 @@ The simplest way to do this is maybe with the [sonarqube docker image](https://s Add the following configuration in **build.gradle** for an **android application** module (i.e. with **com.android.application** plugin): -{{< highlight gradle >}} +```groovy buildscript {   dependencies {       classpath "org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:2.0.1" @@ -76,34 +76,36 @@ sonarqube {       property "sonar.android.lint.report", "build/outputs/lint-results.xml"   } } -{{< /highlight >}} +``` Add the same configuration in **build.gradle** for an **android library** module (i.e. with **com.android.library** plugin) with following modifications: -- Remove « build/intermediates/exploded-aar/**/classes.jar » from library variable as follow: - {{< highlight gradle >}} +- Remove « build/intermediates/exploded-aar/\*\*/classes.jar » from library variable as follow: + + ```groovy def libraries = project.android.sdkDirectory.getPath() + "/platforms/android-23/android.jar" - {{< /highlight >}} + ``` - Remove folder src/androidTest/java from “sonar.tests” property as follow: - {{< highlight gradle >}} + ```groovy property "sonar.tests", "src/test/java" - {{< /highlight >}} + ``` SonarQube configuration for a **java module** is straightforward with a single line in **build.gradle**: -{{< highlight gradle >}} + +```groovy apply plugin: 'org.sonarqube' -{{< /highlight >}} +``` ## Test your configuration Now, you can test your configuration by analyzing your project with a SonarQube server: -{{< highlight bash >}} +```bash ./gradlew clean test jacocoTestReport sonarqube \          -Dsonar.host.url=http://[Sonarqube server IP]:9000 \          --info --stacktrace - {{< /highlight >}} +``` ## Configure SonarQube analysis @@ -115,7 +117,7 @@ This plugin will parse and import android lint report. The configuration above does not provide any code coverage metrics. This can be done with jacoco in **android application and library**: -{{< highlight java >}} +```java buildscript {   dependencies {       classpath "com.dicedmelon.gradle:jacoco-android:0.1.1" @@ -153,17 +155,17 @@ jacocoAndroidUnitTestReport {   html.enabled false   xml.enabled true } -{{< /highlight >}} +``` … and as follow with java modules: -{{< highlight java >}} +```java apply plugin: 'jacoco' jacoco {   toolVersion = "0.7.6.201602180812" } -{{< /highlight >}} +``` ## Result @@ -179,32 +181,32 @@ Android Studio can analyze your code in real time with SonarLint for IntelliJ}} +```java sonarqube {   skipProject = true } -{{< /highlight >}} +``` … or like this from parent **build.gradle**: -{{< highlight gradle >}} +```groovy project(":modulename_to_skip") {   sonarqube {       skipProject = true   } } -{{< /highlight >}} +``` You can overwrite sonar project name and key like this: -{{< highlight java >}} +```java sonarqube {    properties {        property "sonar.projectName", "My project"        property "sonar.projectKey", "com.organisation:my-project"    } } -{{< /highlight >}} +``` ## Limitations @@ -224,13 +226,13 @@ If you want all modules in a single project in SonarQube, you can move SonarQube - StackOverflowError : remove involved rule or source file - Exception while applying a rule : remove rule -{{< highlight bash >}} +```bash Exception applying rule CommentDefaultAccessModifier on file /Users/Jibidus/Development/Sogilis/Repos/sonarqube-for-android-example/android-app/src/main/java/com/sogilis/example/android/app/MainActivity.java, continuing with next rule java.lang.NullPointerException -{{< /highlight >}} +``` - **StackOverflowError** with **sonar-gradle-plugin 2.1** : downgrade to **2.0.1** -- Jenkins does not find XML test report. Check Jenkins does look after */build/test-results/**/*.xml. +- Jenkins does not find XML test report. Check Jenkins does look after _/build/test-results/\*\*/_.xml. - My workspace:module-name is not a valid project or module key. Allowed characters are alphanumeric, `-`, `_`, `.` and `:`, with at least one non-digit. The Default SonarQube project name is the project folder. So if this folder contains invalid character, this could be the explanation. You can set project name and key (see tips above). - :jacocoTestReport SKIPPED: run **test** task before **jacocoTestReport** - org.gradle.internal.jvm.Jvm.getRuntimeJar()Ljava/io/File : incompatible gradle version. Downgrade from gradle 3 to 2. diff --git a/site/content/posts/2017-02-07-inline-comments-pull-requests.md b/blog/_posts/2017-02-07-inline-comments-pull-requests.md similarity index 96% rename from site/content/posts/2017-02-07-inline-comments-pull-requests.md rename to blog/_posts/2017-02-07-inline-comments-pull-requests.md index 3ce5f5b6a..2ad90c783 100644 --- a/site/content/posts/2017-02-07-inline-comments-pull-requests.md +++ b/blog/_posts/2017-02-07-inline-comments-pull-requests.md @@ -1,10 +1,10 @@ --- -title: "Inline Comments: the Stuff that Pull Requests Are Made of" +title: 'Inline Comments: the Stuff that Pull Requests Are Made of' author: Simon Denier date: 2017-02-07T15:49:06+00:00 image: /img/2017-02-Matching_columns-2.png categories: - - DÉVELOPPEMENT + - Développement logiciel tags: - codereview - development @@ -14,15 +14,14 @@ tags: - plugin - pullrequests - tuleap - --- + ## Did you ever wonder how GitHub-like code review works? Fear no more, and discover the intricacies of developing a system for inline comments ! Thanks to GitHub and the like, the practice of code review have become widespread. Even for small projects, one can feel the benefits: catching errors, loopholes, or artificial complexity, sharing the knowledge of how code fulfills its function, enforcing or teaching common guidelines and patterns... Last year, we were asked to develop a Pull Request plugin for the [Tuleap](https://www.tuleap.org/) platform (an open-source forge to track project development). This plugin was designed as an alternative to [Gerrit](https://www.gerritcodereview.com/) (which ships by default), in order to support different team workflows. This project gave us a wonderful opportunity to dive into the intricacies of a web-based code review system: how do you manage the workflow of a pull request, from creation to updates and final merge ? How do you detect conflicts ? What's the best way to display diffs ? And most of all, **how do you implement an inline comments system for code review** ? In this article, we will focus on this last problem as this is the hallmark of any code review system. As we will see, this is not so obvious and requires some conceptualization and computation. But it will serve as a good illustration of the power of [changesets](https://blog.sogilis.com/posts/2015-05-05-demystifying-git-concepts-to-understand/) for any capable VCS (namely Git), as it is an important cornerstone of our solution. - ### What Problem Are We Talking about? In the general picture, the whole process goes something like this: @@ -57,22 +56,22 @@ _An inline comment is a block of data (text) attached to a line in a unidiff._ Here is an example with a dummy unidiff. Ax represents one line content. Deleted lines are prefixed by - and added lines by +. Let's say A3 has been added and you put an inline comment on it. -{{< highlight bash >}} +```bash A1 A2 + A3   ### your inline comment: say something about this change A4 A5 -{{< /highlight >}} +``` Now some changes are made and the pull request is updated. What can happen? For example, the reviewer requested that A3 is no good and should be deleted. The updated unidiff would look like: -{{< highlight bash >}} +```bash A1 A2 A4 A5 -{{< /highlight >}} +``` Line A3 has disappeared. What can we do with the inline comment? It can not be displayed in a meaningful way, next to the line it was attached too, so it appears to be irrelevant, in other words outdated. Fair enough, we seem to have a simple rule to update an inline comment: @@ -80,7 +79,7 @@ _If the inline comment is attached to a line which has disappeared with the last Obviously, if line A3 still existed in the new update, we should have seen the inline comment as still relevant and would have keep it. What about this case? -{{< highlight bash >}} +```bash A1 + B1 + B2 @@ -88,7 +87,7 @@ A1 + A3   ### your inline comment: say something about this change A4 A5 -{{< /highlight >}} +``` Rather than changing the A3 line, the update provides two new lines B1 and B2 (which become parts of the global pull request, including A3). The system can not decide whether the change resolves or even affects the inline comment. Thus, it should still display the comment to let the reviewer decided whether it is relevant or not. Of course, it should still appear next to the A3 line, which means in this case the comment has « moved down » from the third line in the original unidiff to the fifth line in the new unidiff. @@ -113,24 +112,24 @@ Also, let index each content line by its offset in the diff. For starters, it is Let's start with the following content: -{{< highlight bash >}} +```bash A1 A2 A3 A4 A5 -{{< /highlight >}} +``` We create a pull request which inserts a line B1 between A4 and A5. So the original unidiff for the pull request looks like: -{{< highlight bash >}} +```bash 1   A1 2   A2 3   A3 4   A4 5 + B1 6   A5 -{{< /highlight >}} +``` The review goes like this: @@ -146,7 +145,7 @@ How do we know which lines does not change, which are deleted, and which are mov In our example, since B1 change has already been introduced, the update diff looks like: -{{< highlight bash >}} +```bash 1   A1 2 + C1 3   A2 @@ -154,11 +153,11 @@ In our example, since B1 change has already been introduced, the update diff loo 5   B1 6   A4 7   A5 -{{< /highlight >}} +``` What if we juxtapose the offset coordinates from the original diff on this one? -{{< highlight bash >}} +```bash 1 1 A1 2 + C1 2 3 A2 @@ -166,7 +165,7 @@ What if we juxtapose the offset coordinates from the original diff on this one? 4 5 B1 5 6 A4 6 7 A5 -{{< /highlight >}} +``` It is pretty easy to infer which lines were in the previous diff (kept and removed lines) and which ones are only in the new diff (added lines) - and thus, it is pretty easy to compute offsets for **both original and new diffs** on the update diff. We can then translate offsets from one space to the other. @@ -186,20 +185,20 @@ We can detail the procedure to update inline comments on an added or kept line: Right now we just talked about added and untouched lines in the diff. Let's make things a bit more complicated by having both added and deleted lines in the original pull request. What happens if we put inline comments on deleted lines? -{{< highlight bash >}} +```bash 1   A1 2 + B1 3   A2 4 - A3 5   A4 6   A5 -{{< /highlight >}} +``` For example reviewer requests that line A3 should not be deleted. Do the above rules apply? Quite obviously, if the next update does not change the line, the comment holds. On the contrary, if the next update reestablish the line, then we can consider the comment to be outdated. What if lines are moved around as in the updated pull request below? -{{< highlight bash >}} +```bash 1   A1 2 + C1 3 + B1 @@ -207,18 +206,18 @@ What if lines are moved around as in the updated pull request below? 5 - A3 6   A4 7   A5 -{{< /highlight >}} +``` The update diff would look like this (with offsets): -{{< highlight bash >}} +```bash 1 1   A1 2 + C1 3    B1 2 4   A2 4 5   A4 5 6   A5 -{{< /highlight >}} +``` Oups! Line A3 has disappeared from the diff (since it is already deleted) so we can no longer get its coordinates - the hint is that we no longer have the complete suite of offsets from the original diff in the leftmost column. @@ -230,7 +229,7 @@ We will describe each line in a unidiff with a triple coordinates system: offset _Original_ pull request: -{{< highlight bash >}} +```bash // O = diff Offset, B = offset Before, A = offset After O B A 1 1 1   A1 @@ -241,11 +240,11 @@ O B A 6 3   - A3 7 4 6   A4 8 5 7   A5 -{{< /highlight >}} +``` _Update_ diff (from original to final): -{{< highlight bash >}} +```bash O B A 1 1 1   A1 2   2 + C1 @@ -256,11 +255,11 @@ O B A 7 5 6   A2 8 6   - A4 9 7 7   A5 -{{< /highlight >}} +``` _Final_ pull request: -{{< highlight bash >}} +```bash O B A 1 1 1   A1 2   2 + C1 @@ -271,7 +270,7 @@ O B A 7 3   - A3 8 4   - A4 9 5 7   A5 -{{< /highlight >}} +``` With these rules for computing offsets, it is obvious that some offset columns offer a match between diffs. We can use these matches to look up and translate offsets between the diffs. diff --git a/site/content/posts/2017-03-02-collaboration-imprimante-3d-clotoo.md b/blog/_posts/2017-03-02-collaboration-imprimante-3d-clotoo.md similarity index 96% rename from site/content/posts/2017-03-02-collaboration-imprimante-3d-clotoo.md rename to blog/_posts/2017-03-02-collaboration-imprimante-3d-clotoo.md index 5d33a6e13..75b2d6471 100644 --- a/site/content/posts/2017-03-02-collaboration-imprimante-3d-clotoo.md +++ b/blog/_posts/2017-03-02-collaboration-imprimante-3d-clotoo.md @@ -1,11 +1,11 @@ --- -title: "Retour d’expérience : collaboration autour de l’imprimante 3D de Clotoo, « la manufacture particulière »" +title: 'Retour d’expérience : collaboration autour de l’imprimante 3D de Clotoo, « la manufacture particulière »' author: Tiphaine date: 2017-03-02T09:46:27+00:00 image: /img/2017-03-article_clotoo3.jpg categories: - - NOTRE VISION - - TÉMOIGNAGES CLIENTS + - Notre vision + - Témoignages clients tags: - 3Dprint - Clotoo @@ -17,7 +17,6 @@ tags: - monitoring - prototypage - startup - --- **Mi 2016, Sogilis a été sollicitée par lotoo, une start-up grenobloise proposant un réseau collaboratif d’imprimantes 3D entre designers, industriels et particuliers. Notre objectif a été de les accompagner sur la construction d'un système distribué avec des contraintes fortes de résilience, de tolérance aux pannes, couplé à une interface utilisateurs fonctionnelle.** diff --git a/site/content/posts/2017-03-28-bizdej.md b/blog/_posts/2017-03-28-bizdej.md similarity index 98% rename from site/content/posts/2017-03-28-bizdej.md rename to blog/_posts/2017-03-28-bizdej.md index 1e3d5b2c3..0b1c873fe 100644 --- a/site/content/posts/2017-03-28-bizdej.md +++ b/blog/_posts/2017-03-28-bizdej.md @@ -4,7 +4,7 @@ author: Tiphaine date: 2017-03-28T09:22:52+00:00 image: /img/2017-03-Capture-2017-03-27-15.42.58.png categories: - - NOTRE VISION + - Notre vision tags: - accompagnement - bizdej @@ -14,8 +14,8 @@ tags: - PME - projet - startup - --- + ## Les RDV “BIZDEJ” de Sogilis **Industriels, startups ou porteurs de projets, profitez de 2h d’accompagnement pour votre projet innovant - bizdej -** @@ -32,7 +32,6 @@ A l’issue de ces 2h, repartez avec des conseils pragmatiques pour affiner votr - à Lyon le mardi 6 juin : 9h-11h ou 11h-13h - à Grenoble le mercredi 7 juin : 9h-11h ou 11h-13h - Prenez RDV avec nous : biz-dej@sogilis.com ou 06 11 03 33 50 ![bizdej](/img/2017-03-1.png) diff --git a/site/content/posts/2017-04-11-iot-cest-finiiii.md b/blog/_posts/2017-04-11-iot-cest-finiiii.md similarity index 98% rename from site/content/posts/2017-04-11-iot-cest-finiiii.md rename to blog/_posts/2017-04-11-iot-cest-finiiii.md index 05fcffd1e..eedddabec 100644 --- a/site/content/posts/2017-04-11-iot-cest-finiiii.md +++ b/blog/_posts/2017-04-11-iot-cest-finiiii.md @@ -1,10 +1,10 @@ --- -title: "IoT, c’est finiiii : Retour sur le #SiDO2017 vu par Sogilis" +title: 'IoT, c’est finiiii : Retour sur le #SiDO2017 vu par Sogilis' author: Tiphaine date: 2017-04-11T16:10:06+00:00 image: /img/2017-04-SiDO-et-industrie-du-futur.jpg categories: - - NOTRE VISION + - Notre vision tags: - 3Dprint - blockchain @@ -16,8 +16,8 @@ tags: - sido - timetomarket - usages - --- + Le bidouillage technique et l’objet passif, c’est fini. Pendant 2 jours, nous avons parlé d’accès au marché et d’objets utiles. Les 5 & 6 avril 2017 se sont réunis pas moins de 6500 professionnels de la grande famille de l’IoT. Grande famille car c’est dans ce creuset que se rencontrent 3 mondes qui n’ont pas si souvent l’habitude de collaborer : le hard, le soft et le business. # La fin des gadgets diff --git a/site/content/posts/2017-05-23-tuleapcon2017-venez-ecouter-enalean-sogilis-deux-start-up-de-french-tech-in-the-alps-seduisent-grands-groupes.md b/blog/_posts/2017-05-23-tuleapcon2017-venez-ecouter-enalean-sogilis-deux-start-up-de-french-tech-in-the-alps-seduisent-grands-groupes.md similarity index 97% rename from site/content/posts/2017-05-23-tuleapcon2017-venez-ecouter-enalean-sogilis-deux-start-up-de-french-tech-in-the-alps-seduisent-grands-groupes.md rename to blog/_posts/2017-05-23-tuleapcon2017-venez-ecouter-enalean-sogilis-deux-start-up-de-french-tech-in-the-alps-seduisent-grands-groupes.md index 3f293358b..561fcd455 100644 --- a/site/content/posts/2017-05-23-tuleapcon2017-venez-ecouter-enalean-sogilis-deux-start-up-de-french-tech-in-the-alps-seduisent-grands-groupes.md +++ b/blog/_posts/2017-05-23-tuleapcon2017-venez-ecouter-enalean-sogilis-deux-start-up-de-french-tech-in-the-alps-seduisent-grands-groupes.md @@ -1,11 +1,11 @@ --- -title: "TuleapCon2017 : Venez écouter Enalean et Sogilis, deux start-up de la «French Tech in the Alps» qui séduisent les grands groupes" +title: 'TuleapCon2017 : Venez écouter Enalean et Sogilis, deux start-up de la «French Tech in the Alps» qui séduisent les grands groupes' author: Tiphaine date: 2017-05-23T10:08:42+00:00 image: /img/2017-05-tuleapcon2017.png categories: - - LA VIE À SOGILIS - - NOTRE VISION + - La vie à sogilis + - Notre vision tags: - agilité - ALM @@ -16,8 +16,8 @@ tags: - OpenSource - tuleap - TuleapCon2017 - --- + DevOps, livraison continue, open innovation, agilité, quand deux start-up arrivent à faire de ces « buzz words » une réalité, les grands comptes industriels comme **Airbus**, **STMicroelectronics** ou **Orange** suivent la marche. [**Enalean**][1], éditeur du logiciel open source Tuleap, basé à Grenoble et Savoie Technolac, et [**Sogilis**][2], société d'ingénierie informatique basée à Grenoble et Lyon, collaborent depuis plus de 3 ans. Ensemble, les deux sociétés du sillon alpin fournissent des applications logicielles de haute qualité, de véritables avantages concurrentiels pour les industriels. Car si l’avenir d’une start-up peut être lié à un grand compte, le contraire est tout aussi vrai ! diff --git a/site/content/posts/2017-05-31-ergogame-jeu-decouvrir-principes-de-conception-dergonomie-cognitive.md b/blog/_posts/2017-05-31-ergogame-jeu-decouvrir-principes-de-conception-dergonomie-cognitive.md similarity index 90% rename from site/content/posts/2017-05-31-ergogame-jeu-decouvrir-principes-de-conception-dergonomie-cognitive.md rename to blog/_posts/2017-05-31-ergogame-jeu-decouvrir-principes-de-conception-dergonomie-cognitive.md index 5bcbddf34..4d5330f3e 100644 --- a/site/content/posts/2017-05-31-ergogame-jeu-decouvrir-principes-de-conception-dergonomie-cognitive.md +++ b/blog/_posts/2017-05-31-ergogame-jeu-decouvrir-principes-de-conception-dergonomie-cognitive.md @@ -4,8 +4,8 @@ author: Marion et Margaux date: 2017-05-31T15:30:46+00:00 image: /img/2017-05-ergogame.jpeg categories: - - ERGONOMIE - - LA VIE À SOGILIS + - Ergonomie + - La vie à sogilis tags: - ergonomie cognitive - gamification @@ -13,8 +13,8 @@ tags: - sensibilisation - transmission - ux-design - --- + Les Ergonomes à Sogilis depuis quelques mois, nous avons mis en place les Ergogames : un rendez-vous régulier ouvert à tous les membres de l’entreprise pour découvrir des notions d’ ergonomie cognitive par le jeu. À chaque séance, des profils aussi variés que développeurs, DAF, assistante de direction ou chargée de projets innovants se retrouvent pour partager ensemble un moment de détente et d’apprentissage. Retour d’expérience sur la mise en place de ces Ergogames. ![Photo lors d'un ergogame à Lyon](/img/2017-05-ergogame-1024x576.jpeg) @@ -45,10 +45,10 @@ Pour la partie apprentissage, nous avons misé sur des maquettes pour apprendre Par exemple si l’on regarde le [site de cette agence artistique](http://www.agence-adequat.com/fiche.cfm/0-0_389646_kyan_khojandi.html), la lecture est difficile à cause du choix de typographie et de l’utilisation des majuscules. La typographie est déterminante pour une lecture confortable, et l’attention doit être portée sur : -* espacement entre les lettres, entre les mots, entre les lignes -* empattements -* hauteur des lettres -* longueur des lignes et l’alignement +- espacement entre les lettres, entre les mots, entre les lignes +- empattements +- hauteur des lettres +- longueur des lignes et l’alignement [Wikiwand](http://www.wikiwand.com) propose par exemple d’afficher les contenus Wikipédia avec une typographie plus ergonomique que celle de Wikipédia ([voir un article dans Wikiwand](https://www.wikiwand.com/fr/Typographie)), et la possibilité de personnaliser certains attributs. @@ -58,7 +58,7 @@ L’idée est de montrer que même si on a l’impression que c’est du bon sen Pour le format, nous nous sommes inspirées d’un atelier de Julien Champagne, présenté lors des UX Days 2015 (conférence pour les professionnels de l’expérience utilisateur). Il menait une étude quantitative avec ses élèves et avait conçu un site internet qui permettait de tester 4 ou 5 principes d’ ergonomie. -La première étape était, bien entendu, de lister les principes que nous souhaitions aborder. Pour cela, nous avons brainstormé en nous appuyant sur le travail de Bastien et Scapin*, et sur [un article de blog écrit il y a quelques années par Margaux](https://www.margaux-perrin.com/bases-dergo-notions-a-connaitre-et-a-appliquer/), résumant les principes ergo fondamentaux. Nous avons ainsi choisi une quinzaine de principes à présenter et illustrer. Nous avons ensuite regroupé ces principes en 6 grands thèmes, qui formeraient nos séances d’Ergogames. +La première étape était, bien entendu, de lister les principes que nous souhaitions aborder. Pour cela, nous avons brainstormé en nous appuyant sur le travail de Bastien et Scapin\*, et sur [un article de blog écrit il y a quelques années par Margaux](https://www.margaux-perrin.com/bases-dergo-notions-a-connaitre-et-a-appliquer/), résumant les principes ergo fondamentaux. Nous avons ainsi choisi une quinzaine de principes à présenter et illustrer. Nous avons ensuite regroupé ces principes en 6 grands thèmes, qui formeraient nos séances d’Ergogames. Pour l’aspect pratique, nous avons choisi de réaliser des maquettes interactives (avec UX Pin). Pour illustrer les différents principes, nous avons proposé une IHM avec une tâche à accomplir pour chaque principe. Afin de convaincre les participant·e·s du bien-fondé de chaque principe ergo, nous avons essayé de mettre en place une approche scientifique : pour chaque principe, faire tester à la moitié du groupe une version qui applique correctement le principe (le jeu “contrôle”) et une version qui bafoue la règle (le jeu « test »). @@ -67,7 +67,7 @@ Pour l’aspect pratique, nous avons choisi de réaliser des maquettes interacti Les Ergogames se déroulent environ une fois par mois entre midi et deux, dure environ 30 minutes et contient 2 à 3 jeux. Les participant·e·s apportent un ordinateur portable pour deux. 1. Chaque séance (surtout quand il y a de nouvelles personnes) commence par un rappel des “règles du jeu” de l’Ergogame : - > _Nous ne sommes pas là pour évaluer votre capacité à résoudre les exercices, mais pour vous sensibiliser à des principes ergonomiques. C’est normal de ne pas réussir facilement certains exercices, c’est cela que nous cherchons à démontrer. Si la tâche est compliquée, ce n’est pas votre faute : c’est la faute du design !_ + > _Nous ne sommes pas là pour évaluer votre capacité à résoudre les exercices, mais pour vous sensibiliser à des principes ergonomiques. C’est normal de ne pas réussir facilement certains exercices, c’est cela que nous cherchons à démontrer. Si la tâche est compliquée, ce n’est pas votre faute : c’est la faute du design !_ 2. Les 2 liens du premier jeu seront partagés et attribués aux binômes : certains commencent par le test et d’autre par le contrôle. Lorsqu’ils ont réussi la tâche, les binômes testent l’autre lien pour comparer. 3. Nous faisons ensuite une petite présentation du concept en s’appuyant sur le support, en présentant des exemples, et en répondant aux questions. Pendant cette étape, les participant·e·s prennent des notes dans leur passeport ergo. 4. Puis on enchaine sur le jeu suivant. @@ -101,6 +101,6 @@ D’ici là, n’hésitez pas nous contacter pour plus d'informations, ou pour _[Critères Ergonomiques pour l’Évaluation d’Interfaces Utilisateurs](http://blocnotes.iergo.fr/wp-content/uploads/2011/04/criteres.pdf) (version 2.1), J. M. Christian Bastien and Dominique L. Scapin, INRIA, Technical report N° 156 Mai 1993_ -Marion Georges - [@mgeorges_](https://twitter.com/mgeorges_) +Marion Georges - [@mgeorges\_](https://twitter.com/mgeorges_) Margaux Perrin - [@Margauxlergo](https://twitter.com/Margauxlergo) - [www.margaux-perrin.com](http://www.margaux-perrin.com) diff --git a/site/content/posts/2017-06-27-liot-nos-bureaux.md b/blog/_posts/2017-06-27-liot-nos-bureaux.md similarity index 98% rename from site/content/posts/2017-06-27-liot-nos-bureaux.md rename to blog/_posts/2017-06-27-liot-nos-bureaux.md index fa5fd3c6e..b810a8fb5 100644 --- a/site/content/posts/2017-06-27-liot-nos-bureaux.md +++ b/blog/_posts/2017-06-27-liot-nos-bureaux.md @@ -1,13 +1,13 @@ --- title: Expérimentation de l’IOT dans les bureaux Sogilis author: Tiphaine -date: -001-11-30T00:00:00+00:00 +date: 2017-06-27 draft: true image: /img/2017-06-node_closed.jpg categories: - - DÉVELOPPEMENT - + - Développement logiciel --- + Les objets connectés sont de plus en plus présents autour de nous et font partie des axes sur lesquels Sogilis souhaite montrer son savoir faire et mettre en avant son expertise. Après une sollicitation de l'avis des membres de la BU systèmes critiques sur le meilleur moyen d’aborder cette thématique au sein de l'équipe nous avons décidé d’instrumenter les bureaux avec différents capteurs et actionneurs. # Le choix du matériel diff --git a/site/content/posts/2017-07-11-casino-serious-game-innover.md b/blog/_posts/2017-07-11-casino-serious-game-innover.md similarity index 98% rename from site/content/posts/2017-07-11-casino-serious-game-innover.md rename to blog/_posts/2017-07-11-casino-serious-game-innover.md index e30187535..1013855ac 100644 --- a/site/content/posts/2017-07-11-casino-serious-game-innover.md +++ b/blog/_posts/2017-07-11-casino-serious-game-innover.md @@ -1,10 +1,10 @@ --- -title: "Casino Game : Un serious game pour Innover" +title: 'Casino Game : Un serious game pour Innover' author: Arnaud BENISTANT date: 2017-07-11T08:37:13+00:00 image: /img/2017-07-Be-Ready-CasinoGame.jpg categories: - - NOTRE VISION + - Notre vision tags: - agilité - innovation @@ -12,8 +12,8 @@ tags: - lean startup - OpenInnovation - serious game - --- + ![Innovation et Serious Game](/img/2017-07-Be-Ready-1024x442.jpg) Toutes les entreprises ont besoin d’innover pour exister et prendre de nouvelles part de marché : une position un peu trop “conservatrice” sur un marché traditionnel et c’est le risque de l’uberisation dans les prochains mois. Mais pour ces entreprises, PME, ETI ou Grands Groupes, l’innovation est parfois perçue comme une prise de risque. C’est vrai, innover c’est accepter de prendre un risque. Comme tous les risques, ceux-ci peuvent être maîtrisés si l’on comprend quels sont les bons leviers. Regardons en détail les challenges à franchir : diff --git a/site/content/posts/2017-08-08-support-stockage-jpa-mongodb-application-spring-boot.md b/blog/_posts/2017-08-08-support-stockage-jpa-mongodb-application-spring-boot.md similarity index 96% rename from site/content/posts/2017-08-08-support-stockage-jpa-mongodb-application-spring-boot.md rename to blog/_posts/2017-08-08-support-stockage-jpa-mongodb-application-spring-boot.md index 072adee7a..81099be71 100644 --- a/site/content/posts/2017-08-08-support-stockage-jpa-mongodb-application-spring-boot.md +++ b/blog/_posts/2017-08-08-support-stockage-jpa-mongodb-application-spring-boot.md @@ -3,9 +3,9 @@ title: Support du stockage JPA et MongoDB dans une application Spring-boot author: Alexandre et Jean-Baptiste date: 2017-08-08T15:32:27+00:00 categories: - - DÉVELOPPEMENT - + - Développement logiciel --- + Dans le cadre d’un projet sur lequel nous travaillons actuellement, nous avons été amenés à implémenter un service web spring-boot capable de stocker des objets relativement simples dans une base de données. ![Support du stockage JPA et MongoDB dans une application Spring-boot - header](/img/2017-07-blog-header-2-1024x309.png) @@ -68,7 +68,7 @@ Il suffit alors d’annoter une même classe avec ces 2 annotations, ce qui évi Toute entité doit pouvoir être identifiée de manière unique. Pour cela, il existe aussi 2 annotations distinctes, mais qui ont le même nom (_@Id_) : - JPA : _javax.persistence.Id_ -- MongoDB : _org.springframework.data.annotation.Id_ ([permet de mapper la colonne sur l’identifiant natif MongoDB : _id](http://docs.spring.io/spring-data/data-mongo/docs/1.10.4.RELEASE/reference/html/#mongo-template.id-handling)) +- MongoDB : _org.springframework.data.annotation.Id_ ([permet de mapper la colonne sur l’identifiant natif MongoDB : \_id](http://docs.spring.io/spring-data/data-mongo/docs/1.10.4.RELEASE/reference/html/#mongo-template.id-handling)) Attention au type de cet identifiant et comment il sera défini, il doit être à la fois compatible avec JPA et MongoDB. @@ -76,7 +76,7 @@ Bien sûr, les annotations spécifiques à JPA/Hibernate (tel que _@Id_, _@Colum Nous avons par exemple utilisé [UUID][7] pour générer l’identifiant pour JPA, l’identifiant MongoDB étant généré automatiquement. -{{< highlight java >}} +```java @Entity @Document public class Person { @@ -96,7 +96,7 @@ public class Person { // Setters and Getters are missing } -{{< /highlight >}} +``` # Sélection du type de persistance @@ -123,9 +123,9 @@ Voici celles concernées par MongoDB : Pour la désactivation d’auto-configurations, une première solution consiste à utiliser l’annotation _@SpringBootApplication_ avec le paramètre exclude : -{{< highlight java >}} +```java @SpringBootApplication(exclude = {MongoAutoConfiguration.class, MongoDataAutoConfiguration.class}) -{{< /highlight >}} +``` Cette solution n’est pas satisfaisante car il serait alors nécessaire d’avoir 2 classes avec cette annotation (une pour JPA et une autre pour MongoDB), et il n’est pas possible d’avoir 2 classes _@SpringBootApplication_ dans une application Spring Boot. @@ -135,18 +135,18 @@ Spring Boot se charge alors de charger le bon fichier en fonction du profil cour _application-jpa.properties_ -{{< highlight java >}} +```java spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration,\ org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration -{{< /highlight >}} +``` _application-mongodb.properties_ -{{< highlight java >}} +```java spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration,\ org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration -{{< /highlight >}} +``` ## Repositories @@ -156,34 +156,34 @@ Pour empêcher cela, des propriétés Spring peuvent être utilisées : application-jpa.properties -{{< highlight java >}} +```java spring.data.jpa.repositories.enabled=false -{{< /highlight >}} +``` application-mongo.properties -{{< highlight java >}} +```java spring.data.mongodb.repositories.enabled=false -{{< /highlight >}} +``` ## Configuration Il suffit alors de spécifier les propriétés de configuration relatives à chaque profil. Par exemple, dans le cas d’une connexion à Postgres, on peut ajouter au profil _jpa_ les propriétés suivantes : -{{< highlight java >}} +```java spring.datasource.url=jdbc:postgresql://localhost:5432/spring-boot-jpa-mongo-exemple spring.datasource.username=postgres spring.datasource.password=postgres spring.jpa.hibernate.ddl-auto=create spring.jpa.database-platform=org.hibernate.dialect.PostgreSQLDialect -{{< /highlight >}} +``` Pour une connexion à mongodb (sans sécurité), on peut utiliser ceci dans le profil _mongodb_ : -{{< highlight java >}} +```java spring.data.mongodb.uri=mongodb://localhost:27017/spring-boot-jpa-mongo-exemple -{{< /highlight >}} +``` # Tester la couche de persistence @@ -195,7 +195,7 @@ Il nous suffit alors de créer une classe _PersonRepositoryJpaTest_ annotée par Il faut ensuite que ces 2 classes jouent les mêmes tests. Ceci peut être réalisé en délégant chaque test à une classe unique (_PersonRepositoryTester_) utilisée pour tous les profils. -{{< highlight java >}} +```java @RunWith(SpringRunner.class) @SpringBootTest @Transactional @@ -225,14 +225,14 @@ public class PersonRepositoryTester { } } -{{< /highlight >}} +``` Pour les tests, il est possible d’utiliser une base de donnée embarquée, sans aucune configuration spécifique, il suffit d’ajouter les dépendances de test nécessaires : -{{< highlight java >}} +```java testCompile 'com.h2database:h2' // JPA testCompile 'de.flapdoodle.embed:de.flapdoodle.embed.mongo' // MongoDB -{{< /highlight >}} +``` A noter que l’annotation Spring _@Transactional_ ne fonctionne que pour les tests JPA. Pour MongoDB, il est donc nécessaire de gérer à la main (avec _@Before_ par exemple) la suppression des données entre chaque test. @@ -246,7 +246,7 @@ Ainsi, avec la gestion par profil Spring, il est très facile de mettre en place L’[audit][9] fait partie des fonctionnalités Spring qui nécessitent une configuration spécifique par type de persistance. En effet, il faut ajouter l’annotation _@EnableJpaAuditing_ pour JPA et _@EnableMongoAuditing_ pour MongoDB. -{{< highlight java >}} +```java @Configuration @EnableJpaAuditing @Profile("jpa") @@ -258,7 +258,7 @@ public class JpaConfiguration { @EnableMongoAuditing public class MongoConfiguration { } -{{< /highlight >}} +``` A noter que les annotations d’audit (_@CreatedBy_, _@CreatedDate_ ...) a placer sur les entités sont indépendantes du type de persistance choisi. diff --git a/site/content/posts/2017-08-17-9-mois-a-sogilis.md b/blog/_posts/2017-08-17-9-mois-a-sogilis.md similarity index 89% rename from site/content/posts/2017-08-17-9-mois-a-sogilis.md rename to blog/_posts/2017-08-17-9-mois-a-sogilis.md index b8c2b6110..513fab510 100644 --- a/site/content/posts/2017-08-17-9-mois-a-sogilis.md +++ b/blog/_posts/2017-08-17-9-mois-a-sogilis.md @@ -4,13 +4,13 @@ author: Margaux date: 2017-08-17T11:45:28+00:00 image: /img/2017-08-margaux-au-bureau.jpg categories: - - LA VIE À SOGILIS + - La vie à sogilis tags: - entreprise-libérée - ergonomie - témoignage - --- + C’est l’été, au moment où j’écris ces lignes c’est même le 15 août, vous êtes peut-être en vacances ou en week-end prolongé, ou vous profitez juste d’un jour férié pour prendre un peu de repos, boire une bière en terrasse ou vous prélasser au bord de la piscine de vos beaux-parents. Moi, aujourd’hui, je travaille (pourquoi ? J’y reviendrai), et comme la période estivale est moins chargée, j’ai décidé d’écrire un article de blog pour faire le bilan de mes 9 premiers mois à Sogilis. Un peu de contexte pour commencer. @@ -29,7 +29,6 @@ Après 2 ans dans cette entreprise et une expérience vraiment enrichissante, le Lorsque je suis arrivée dans l’entreprise où j’étais précédemment, une des personnes qui m’a le mieux accueillie était Luc Jeanniard, scrum-master arrivé un mois avant moi, en charge de mettre en place l’agilité dans l’entreprise. Il venait de Sogilis, et est reparti pour Sogilis 18 mois plus tard ([son retour d’expérience ici](https://blog.sogilis.com/posts/2016-08-30-sogilis-entreprise-pas-comme-autres/)). Nous avons continué à déjeuner ensemble régulièrement, et son discours sur Sogilis était très attrayant : **une entreprise libérée, aux horaires souples, basée sur la confiance et l’implication, avec une grande variété de projets courts**. Tout ce dont je rêvais. Il m’a fallu tout de même une dizaine de mois pour sauter le pas, entre son départ et ma candidature. Tout est une question de balance bénéfice/risque : en juillet 2016 le bénéfice semblait conséquent et le risque faible, il était temps ! Dernier argument pour Sogilis : **l’existence de Sogilis Lyon**. Pour des raisons personnelles, je souhaitais retourner à Lyon à moyen terme. Trouver un boulot à Lyon il y a un an m’aurait mis dans une situation compliquée (trop tôt), et trouver une entreprise 100% grenobloise me condamnait à changer à nouveau d’entreprise un an plus tard, ou à renoncer à mon projet de retour à Lyon. La situation de Sogilis et les échos que j’en avais me laissaient supposer un possible basculement de Grenoble à Lyon le moment venu. - # Processus de recrutement Luc m’avait prévenu que les équipes se retrouvaient le jeudi en fin de matinée pour un point recrutement, j’ai donc envoyé ma candidature un jeudi matin. Le lendemain, je recevais un mail de Myriam, chargée de communication, me proposant un **entretien avec deux développeurs et elle-même**. Elle me disait d’apporter un ou plusieurs **exemples de projets personnels pour les leur présenter**. @@ -44,9 +43,9 @@ Lors de l’entretien, on a parlé de Sogilis, de moi, de mes envies, de mes pra En 9 mois, j’ai travaillé sur 10 projets différents dans des **secteurs très variés** : immobilier, militaire, industrie, agronomie, énergie, traduction… et avec des approches différentes : -* quelques projets pour lesquels j’ai fait un **travail court de consultante interne** (maquettage essentiellement, tests utilisateurs de temps à autre) -* quelques projets intermédiaires courts, pour lesquels j’ai **travaillé en équipe** une dizaine de jours répartis sur 2 mois maximum, sur des aspects variés **en phase amont des projets** : entretiens, story mapping, ateliers de co-conception, maquettage, rédaction de cahier des charges... -* un **gros projet** sur lequel j’ai travaillé 45 jours depuis 8 mois, faisant un **travail d’ergonome plus abouti et m’impliquant dans la gestion du projet** au quotidien en tant que représentante utilisateur et proxy PO. Sur ce projet, je fais **partie intégrante de l’équipe** de développement. C’est très important pour moi de me sentir appartenir à un groupe, et je le trouve aujourd’hui essentiellement grâce à ce projet. +- quelques projets pour lesquels j’ai fait un **travail court de consultante interne** (maquettage essentiellement, tests utilisateurs de temps à autre) +- quelques projets intermédiaires courts, pour lesquels j’ai **travaillé en équipe** une dizaine de jours répartis sur 2 mois maximum, sur des aspects variés **en phase amont des projets** : entretiens, story mapping, ateliers de co-conception, maquettage, rédaction de cahier des charges... +- un **gros projet** sur lequel j’ai travaillé 45 jours depuis 8 mois, faisant un **travail d’ergonome plus abouti et m’impliquant dans la gestion du projet** au quotidien en tant que représentante utilisateur et proxy PO. Sur ce projet, je fais **partie intégrante de l’équipe** de développement. C’est très important pour moi de me sentir appartenir à un groupe, et je le trouve aujourd’hui essentiellement grâce à ce projet. ## Avant-ventes @@ -56,19 +55,19 @@ J’ai également participé à une douzaine d’avant-ventes, dans des secteurs Le besoin d'ergonomie, au regard de la taille de l'entreprise, est cyclique et me laisse du temps pour d'autres activités. En particulier, je m’implique dans les questions de communication interne et externe : -* mise en place d’une landing page pour Sogilis Australie +- mise en place d’une landing page pour Sogilis Australie ![licorne-sticker](/img/2017-08-licorne-sticker.png) -* (co-)écriture d’articles de blog -* participation à des conférences en tant qu’oratrice ou visiteuse (live-tweet, sketchnotes, networking…) -* mise en place d’un workshop interne pour réfléchir à des supports de communication -* création d’un sticker licorne -* création d’un kakémono pour une conférence -* cocréation de supports de communication pour des évènements organisés par Sogilis -* mise à jour du livret d’accueil -* mise en place d’une dynamique de partage entre Sogilis Lyon et Sogilis Grenoble : - * avec Marion, ergonome à Lyon, nous avons organisé des _Ergogames_ [petits jeux pour sensibiliser aux pratiques d’ergonomie, (on en parle ici](https://blog.sogilis.com/posts/2017-05-31-ergogame-jeu-decouvrir-principes-de-conception-dergonomie-cognitive/)) ; - * avec Kevin, développeur à Lyon, nous avons mis en place un challenge photo (une photo par mois sur un thème donné avec photo gagnante publiée sur Twitter, permettant également de renouveler le stock de photos pour la communication externe) -* organisation des premières rétrospectives de l’équipe “Cloud Native” +- (co-)écriture d’articles de blog +- participation à des conférences en tant qu’oratrice ou visiteuse (live-tweet, sketchnotes, networking…) +- mise en place d’un workshop interne pour réfléchir à des supports de communication +- création d’un sticker licorne +- création d’un kakémono pour une conférence +- cocréation de supports de communication pour des évènements organisés par Sogilis +- mise à jour du livret d’accueil +- mise en place d’une dynamique de partage entre Sogilis Lyon et Sogilis Grenoble : + - avec Marion, ergonome à Lyon, nous avons organisé des _Ergogames_ [petits jeux pour sensibiliser aux pratiques d’ergonomie, (on en parle ici](https://blog.sogilis.com/posts/2017-05-31-ergogame-jeu-decouvrir-principes-de-conception-dergonomie-cognitive/)) ; + - avec Kevin, développeur à Lyon, nous avons mis en place un challenge photo (une photo par mois sur un thème donné avec photo gagnante publiée sur Twitter, permettant également de renouveler le stock de photos pour la communication externe) +- organisation des premières rétrospectives de l’équipe “Cloud Native” Enfin, de manière ponctuelle, je participe au recrutement et je fais un peu de business development. @@ -78,8 +77,8 @@ Enfin, de manière ponctuelle, je participe au recrutement et je fais un peu de Si l’on repart de mes motivations à quitter mon entreprise précédente, on peut résumer ça en 2 axes : -* **mon travail et mon impact** : besoin de me sentir utile, envie de travailler sur des projets plus courts et plus variés pour apprendre de nouvelles choses et voir rapidement le fruit de mon travail ; -* **le management / l’organisation du travail** : envie de trouver une entreprise en accord avec mes valeurs, que j’avais d’ailleurs choisi d’afficher clairement sur mon CV (justice, solidarité, responsabilité, éthique, transparence). +- **mon travail et mon impact** : besoin de me sentir utile, envie de travailler sur des projets plus courts et plus variés pour apprendre de nouvelles choses et voir rapidement le fruit de mon travail ; +- **le management / l’organisation du travail** : envie de trouver une entreprise en accord avec mes valeurs, que j’avais d’ailleurs choisi d’afficher clairement sur mon CV (justice, solidarité, responsabilité, éthique, transparence). Neuf mois plus tard, le bilan. @@ -93,12 +92,12 @@ Je suis également beaucoup plus au contact des clients que je n’ai pu l’êt S’il y a toujours des choses à améliorer, beaucoup d’éléments sont conformes à mes attentes, et j’en suis ravie : -* **la flexibilité** : je suis en train de travailler le 15 août pour me libérer du temps dans les jours à venir pour visiter des appartements (pour déménager à Lyon, objectif atteint !). Si je veux faire des horaires décalés c’est mon problème, si je veux travailler de chez moi je le fais. Tant que ma manière de travailler n’a pas d’impact négatif sur les projets que je mène, je fais ce que je veux. -* **la confiance** : il n'y a personne qui vérifie mes horaires, personne qui contrôle 20 fois mon travail, personne qui vient derrière mon épaule pour regarder ce sur quoi je travaille, personne qui m'interdit de prévoir un goûter à 16h30 car ça va réduire la productivité… -* **la bienveillance** : même quand ça ne se passe pas comme on l’aurait voulu, on part globalement du principe que chaque membre de l’équipe a fait du mieux qu’il a pu avec les informations qu’il avait. Il n’y a jamais qui que ce soit qui surgit en hurlant sur les équipes car un problème est survenu en prod’. Jamais un middle manager qui reporte son stress ou la faute sur les équipes de développement (faut dire, y’a pas de middle manager à Sogilis, ça évite ce genre de problème). On s'y habitue vite et c'est vraiment agréable. -* **la transparence** : nous avons accès à toutes les finances de l’entreprise. La vision de l’entreprise portée par Christophe est régulièrement communiquée à toute l’entreprise. Lorsqu’une décision est prise par la direction en désaccord avec les équipes elle est expliquée, avec pédagogie. -* **l’implication de tous** : ça peut toujours être mieux, mais il n’y a vraiment pas photo par rapport à de nombreuses entreprises. Ici les gens sont tous impliqués dans les projets de nos clients et dans la vie de l’entreprise. Je ne vois personne glander toute la journée. Je ne vois même personne qui fasse son boulot correctement mais sans conviction. Je ne vois personne se cacher dans un coin pour ne pas participer aux tâches transverses liées à la vie de l’entreprise. -* **la liberté d’entreprendre et le droit à l’erreur** : si on a une idée, une envie, une méthode que l’on souhaite tester, on y va ! Pas besoin de faire valider par qui que ce soit, seulement consulter les personnes impactées. On ne se retrouve pas dans la situation malheureusement si courante en entreprise où un·e salarié·e arrive hyper motivé·e, plein·e d’idées et d’initiatives, et se désengage après 6 mois passés dans l’entreprise à entendre des “oui mais...” en réponse à ses propositions. Ainsi, j’ai pu très facilement mettre en place les _ergogames_, le challenge photo, ou un workshop communication. +- **la flexibilité** : je suis en train de travailler le 15 août pour me libérer du temps dans les jours à venir pour visiter des appartements (pour déménager à Lyon, objectif atteint !). Si je veux faire des horaires décalés c’est mon problème, si je veux travailler de chez moi je le fais. Tant que ma manière de travailler n’a pas d’impact négatif sur les projets que je mène, je fais ce que je veux. +- **la confiance** : il n'y a personne qui vérifie mes horaires, personne qui contrôle 20 fois mon travail, personne qui vient derrière mon épaule pour regarder ce sur quoi je travaille, personne qui m'interdit de prévoir un goûter à 16h30 car ça va réduire la productivité… +- **la bienveillance** : même quand ça ne se passe pas comme on l’aurait voulu, on part globalement du principe que chaque membre de l’équipe a fait du mieux qu’il a pu avec les informations qu’il avait. Il n’y a jamais qui que ce soit qui surgit en hurlant sur les équipes car un problème est survenu en prod’. Jamais un middle manager qui reporte son stress ou la faute sur les équipes de développement (faut dire, y’a pas de middle manager à Sogilis, ça évite ce genre de problème). On s'y habitue vite et c'est vraiment agréable. +- **la transparence** : nous avons accès à toutes les finances de l’entreprise. La vision de l’entreprise portée par Christophe est régulièrement communiquée à toute l’entreprise. Lorsqu’une décision est prise par la direction en désaccord avec les équipes elle est expliquée, avec pédagogie. +- **l’implication de tous** : ça peut toujours être mieux, mais il n’y a vraiment pas photo par rapport à de nombreuses entreprises. Ici les gens sont tous impliqués dans les projets de nos clients et dans la vie de l’entreprise. Je ne vois personne glander toute la journée. Je ne vois même personne qui fasse son boulot correctement mais sans conviction. Je ne vois personne se cacher dans un coin pour ne pas participer aux tâches transverses liées à la vie de l’entreprise. +- **la liberté d’entreprendre et le droit à l’erreur** : si on a une idée, une envie, une méthode que l’on souhaite tester, on y va ! Pas besoin de faire valider par qui que ce soit, seulement consulter les personnes impactées. On ne se retrouve pas dans la situation malheureusement si courante en entreprise où un·e salarié·e arrive hyper motivé·e, plein·e d’idées et d’initiatives, et se désengage après 6 mois passés dans l’entreprise à entendre des “oui mais...” en réponse à ses propositions. Ainsi, j’ai pu très facilement mettre en place les _ergogames_, le challenge photo, ou un workshop communication. Que celles et ceux qui ne croient pas aux entreprises basées sur ces valeurs viennent faire un stage à Sogilis ! diff --git a/site/content/posts/2017-08-21-tests-utilisateurs-sauvages-retour-dexperience.md b/blog/_posts/2017-08-21-tests-utilisateurs-sauvages-retour-dexperience.md similarity index 99% rename from site/content/posts/2017-08-21-tests-utilisateurs-sauvages-retour-dexperience.md rename to blog/_posts/2017-08-21-tests-utilisateurs-sauvages-retour-dexperience.md index 91653af87..e36e7d3d2 100644 --- a/site/content/posts/2017-08-21-tests-utilisateurs-sauvages-retour-dexperience.md +++ b/blog/_posts/2017-08-21-tests-utilisateurs-sauvages-retour-dexperience.md @@ -1,12 +1,12 @@ --- title: "Tests utilisateurs 'sauvages' : retour d’expérience" author: Sogilis -date: -001-11-30T00:00:00+00:00 +date: 2017-08-21 draft: true categories: - - ERGONOMIE - + - Ergonomie --- + # Qu’est-ce qu’un test utilisateur ? Le test utilisateur est un outil d’analyse permettant de vérifier que l’interface imaginée est adaptée aux utilisateurs cibles. Concrètement, ces utilisateurs vont utiliser l’interface comme un vrai site web, ou une vraie application. Ils seront guidés par un ou plusieurs scénarios préparés par l’ergonome. Celui-ci va pouvoir répondre à plusieurs hypothèses au cours du test utilisateur : diff --git a/site/content/posts/2017-10-11-wrong-usage-of-extern-keyword-in-c.md b/blog/_posts/2017-10-11-wrong-usage-of-extern-keyword-in-c.md similarity index 98% rename from site/content/posts/2017-10-11-wrong-usage-of-extern-keyword-in-c.md rename to blog/_posts/2017-10-11-wrong-usage-of-extern-keyword-in-c.md index 1af577344..3edb68504 100644 --- a/site/content/posts/2017-10-11-wrong-usage-of-extern-keyword-in-c.md +++ b/blog/_posts/2017-10-11-wrong-usage-of-extern-keyword-in-c.md @@ -1,12 +1,12 @@ --- -title: "Compiling & Dangerous : wrong usage of extern keyword in C" +title: 'Compiling & Dangerous : wrong usage of extern keyword in C' author: Victor Lambret date: 2017-10-11T13:27:23+00:00 image: /img/2017-10-first_image.png categories: - - DÉVELOPPEMENT - + - Développement logiciel --- + # Compiling & Dangerous {#compiling-dangerous} C is not a strong typed language but it can run some basic checks. Knowing that, C developers are generally careful because debugging type problems is annoying and can be limited with some simple good practices. @@ -67,7 +67,7 @@ By using the `extern` mechanism you tell the compiler: I declared something that It can be verified by looking at object files: -{{< highlight bash >}} +```bash victor@sogilis$ sh gcc -c main2.c victor@sogilis$ sh gcc -c version3.c victor@sogilis$ readelf -s main2.o version3.o @@ -78,7 +78,7 @@ Num : Value Size Type Bind Vis Ndx Name File: version3.o Num: Value Size Type Bind Vis Ndx Name 7: 0000000000000000 1 OBJECT GLOBAL DEFAULT 2 version -{{< /highlight >}} +``` We can see that in `main2.o` `version` size is 0 and has `NOTYPE`. diff --git a/site/content/posts/2017-11-07-end-to-end-testing-chrome-headless-squarescale.md b/blog/_posts/2017-11-07-end-to-end-testing-chrome-headless-squarescale.md similarity index 94% rename from site/content/posts/2017-11-07-end-to-end-testing-chrome-headless-squarescale.md rename to blog/_posts/2017-11-07-end-to-end-testing-chrome-headless-squarescale.md index aaa3755be..e3b28eaeb 100644 --- a/site/content/posts/2017-11-07-end-to-end-testing-chrome-headless-squarescale.md +++ b/blog/_posts/2017-11-07-end-to-end-testing-chrome-headless-squarescale.md @@ -4,15 +4,15 @@ author: Adrien Hamraoui date: 2017-11-07T12:32:09+00:00 image: /img/2017-11-preview-full-s_600ppi.png categories: - - DÉVELOPPEMENT + - Développement logiciel tags: - chrome headless - chromeless - end-to-end testing - puppeteer - squarescale - --- + # End-to-end testing with chrome headless at SquareScale {#end-to-end-testing-with-chrome-headless-at-squarescale} We just completed writing our first tests using chrome with [Puppeteer][1] at [SquareScale][2] and we are very proud of it. That’s why we really wanted to share it with everybody. But before diving in to how we test our frontend application, we would like to say a few words about how we work on features, our workflow and how we realized that we needed automated tests in a browser. @@ -37,11 +37,9 @@ The first milestone for me was to set up the whole environment and process with The API is really easy to handle. As you can see on their website it is very straight forward, `goto(url)`, `click()`, `type()`. In the end, showing the homepage’s URL code was really tiny: -{{< highlight js >}} -const exists = await chromeless -.goto(sqsc_url) -.html() -{{< /highlight >}} +```js +const exists = await chromeless.goto(sqsc_url).html(); +``` I then decided to write a more complex test which would check if a user could login from the front page. But I run into my second problem, different executions of my test led to different results. Sometimes timeouts, sometimes it could not find the right DOM element. Moreover the whole team had to work for the imminent release and continuing on this project became less important. I felt very disappointed to drop one week of work. I also felt a bit exhausted to have worked one week just to package chrome into docker to finally see that the library I chose was not reliable. On the top of that, the number of contributions on the project, which were quite high when I started to use the library, started to decrease. @@ -57,51 +55,51 @@ Here is the code that allows us to instantiate a new chrome instance before ever src/setup/browser.js -{{< highlight js >}} +```js const puppeteer = require('puppeteer'); beforeEach(async () => { jest.setTimeout(2400000); // 40mn by test browser = await puppeteer.launch({ - args: [ - '--no-sandbox', - '--disable-setuid-sandbox' - ] + args: ['--no-sandbox', '--disable-setuid-sandbox'], }); page = await browser.newPage(); // Capture logging - page.on('console', (...args) => console.log.apply(console, ['[Browser]', ...args])); + page.on('console', (...args) => + console.log.apply(console, ['[Browser]', ...args]) + ); }); afterEach(async () => { await browser.close(); }); -{{< /highlight >}} +``` src/jest.config.js -{{< highlight js >}} +```js const env = process.env.ENV; const sqsc_url = env === 'dev' ? 'squarescale.local' : `squarescale.${env}`; module.exports = { setupTestFrameworkScriptFile: './setup/browser.js', - globals: { // available in all tests + globals: { + // available in all tests browser: null, page: null, - sqsc_url - } + sqsc_url, + }, }; -{{< /highlight >}} +``` We chose to run a new instance of chrome for every test to keep them separated from each others. We can also imagine that it will be better when we will run them in parallel. In the end, it add a few seconds of overhead but it is non significant compared to the time taken by the test itself. ### Writing scenarii with Puppeteer, ensure login feature works {#writing-scenarii-with-puppeteer-ensure-login-feature-works} -{{< highlight js >}} +```js const login = process.env.GITHUB_LOGIN; const password = process.env.GITHUB_PASSWORD; const secret = process.env.GITHUB_SECRET; @@ -125,12 +123,11 @@ try { await page.click('.btn'); await page.waitForNavigation({ waitUntil: 'networkidle' }); await page.waitForSelector('html.signed'); -} -catch (e) { +} catch (e) { console.error(e); throw 'Exception during login'; } -{{< /highlight >}} +``` ### Running with Docker {#running-with-docker} @@ -138,7 +135,7 @@ The Dockerfile is also very simple, we chose to use a node js image and to add s Credit to . -{{< highlight docker >}} +```docker FROM node:8-slim RUN apt-get update && \ @@ -151,19 +148,19 @@ RUN apt-get update && \ apt-get clean && \ apt-get autoremove -y && \ rm -rf /var/lib/apt/lists/* -{{< /highlight >}} +``` We simply have to use the image to run our tests with Jest: -{{< highlight bash >}} +```bash docker run --rm --name 'qa-front' qa-front yarn run test -{{< /highlight >}} +``` ### Jenkins {#jenkins} Our Jenkins simply use the docker container we made to run the tests (notice `agent { dockerfile true }`). We configured it to provide the secrets, and to run the tests every 6 hours: -{{< highlight js >}} +```js pipeline { triggers { cron('0 5,11,17,23 * * *') @@ -203,7 +200,7 @@ pipeline { } } } -{{< /highlight >}} +``` ## The final word {#the-final-word} diff --git a/site/content/posts/2017-11-20-salarie-etranger-visa-travail-france.md b/blog/_posts/2017-11-20-salarie-etranger-visa-travail-france.md similarity index 58% rename from site/content/posts/2017-11-20-salarie-etranger-visa-travail-france.md rename to blog/_posts/2017-11-20-salarie-etranger-visa-travail-france.md index 17c1ededc..5d48537ce 100644 --- a/site/content/posts/2017-11-20-salarie-etranger-visa-travail-france.md +++ b/blog/_posts/2017-11-20-salarie-etranger-visa-travail-france.md @@ -1,10 +1,10 @@ --- -title: "Recruter un salarié étranger pour travailler en France : Le visa, ce parcours du combattant ?!" +title: 'Recruter un salarié étranger pour travailler en France : Le visa, ce parcours du combattant ?!' author: Tiphaine Guittat date: 2017-11-20T15:20:54+00:00 image: /img/2017-11-ofii-min.jpg categories: - - LA VIE À SOGILIS + - La vie à sogilis tags: - ambassade - australie @@ -13,7 +13,6 @@ tags: - OFII - Visa - VLS-TS - --- Début 2017, Sogilis décide de recruter un salarié étranger, australien plus précisément, pour intégrer son équipe de développeurs web. Voici la procédure détaillée que nous avons du suivre (Visa VLS-TS) pour que Haze puisse entrer et travailler pour notre PME française. @@ -24,17 +23,17 @@ Notre patron, Christophe, rejoint notre collègue Etienne pour quelques semaines Je vais tenter de vous décrire de manière synthétique et détaillée l'ensemble des étapes que j'ai suivies pour faire en sorte qu'il puisse nous rejoindre début septembre à Grenoble 🙂 -* L'embauche de Haze se fera en CDI. Nous pouvons donc entamer une procédure de VLS-TS (Visa Long Séjour valant Titre de Séjour) pour un motif salarié, et non pas travailleur temporaire (pour les CDD) : Carte de séjour « passeport talent » (4 ans maximum) « ][1]  car les conditions sont assez strictes, que ce soit pour la qualification « salarié qualifié », « recrutement dans une Jeune Entreprise Innovante » ou « emploi hautement qualifié ». +- L'embauche de Haze se fera en CDI. Nous pouvons donc entamer une procédure de VLS-TS (Visa Long Séjour valant Titre de Séjour) pour un motif salarié, et non pas travailleur temporaire (pour les CDD) : Carte de séjour « passeport talent » (4 ans maximum) « ][1]  car les conditions sont assez strictes, que ce soit pour la qualification « salarié qualifié », « recrutement dans une Jeune Entreprise Innovante » ou « emploi hautement qualifié ». _« Le visa de long séjour valant titre de séjour (VLS-TS) permet d'entrer en France et d'y séjourner de 4 mois à 1 an sans avoir à demander tout de suite un titre de séjour. Il s'agit d'une vignette apposée sur votre passeport par l'administration. »_ ![](/img/2017-10-Capture-2017-10-30-16.06.45.png) -* Avril 2017 : La première étape est de prouver la **« non-opposabilité » de l'offre d'emploi sur le marché français**. Il est important que l'entreprise puisse justifier que si elle va chercher à l'étranger les compétences, c'est parce qu'elle n'a pas pu les trouver en France. Il nous a donc fallu publier une offre d'emploi sur Pôle Emploi. **(Attention, les annonces sur d'autres sites d'emploi ne fonctionnent pas, il faut publier sur PE)**, sur une durée d'au moins 3 semaines. N'ayant reçu qu'une seule réponse à cette annonce, non pertinente, nous avons ainsi pu justifier de notre besoin de compétences étrangères. +- Avril 2017 : La première étape est de prouver la **« non-opposabilité » de l'offre d'emploi sur le marché français**. Il est important que l'entreprise puisse justifier que si elle va chercher à l'étranger les compétences, c'est parce qu'elle n'a pas pu les trouver en France. Il nous a donc fallu publier une offre d'emploi sur Pôle Emploi. **(Attention, les annonces sur d'autres sites d'emploi ne fonctionnent pas, il faut publier sur PE)**, sur une durée d'au moins 3 semaines. N'ayant reçu qu'une seule réponse à cette annonce, non pertinente, nous avons ainsi pu justifier de notre besoin de compétences étrangères. ![](/img/2017-10-Capture-2017-10-30-16.20.18.png) -* Mai 2017 : Nous avons ensuite complété le **formulaire CERFA 15187*02**, permettant d'enclencher la procédure au niveau de la DIRECCTE (Direction régionale des entreprises, de la concurrence, de la consommation, du travail et de l'emploi) de notre département. +- Mai 2017 : Nous avons ensuite complété le **formulaire CERFA 15187\*02**, permettant d'enclencher la procédure au niveau de la DIRECCTE (Direction régionale des entreprises, de la concurrence, de la consommation, du travail et de l'emploi) de notre département. ![](/img/2017-10-Capture-2017-10-30-16.26.58.png) @@ -42,13 +41,13 @@ Ce document, qui comporte 5 pages, est fondamental pour l'instruction du dossier **La liste des pièces à fournir est la suivante :** -* Certificats de travail du salarié dans son pays d'origine. En l'occurrence, en Australie, les employeurs ne rédigent pas de « certificats de travail ». Nous avons donc joint les 3 derniers contrats de travail de notre futur collègue. -* Une copie de son passeport -* Copie de son permis de conduire -* et une copie de son diplôme -* son CV détaillé -* la copie de l'offre publié sur Pôle Emploi, sa date de publication initiale et le nombre de renouvellements. -* La copie de son futur contrat de travail français, mentionnant CDI +- Certificats de travail du salarié dans son pays d'origine. En l'occurrence, en Australie, les employeurs ne rédigent pas de « certificats de travail ». Nous avons donc joint les 3 derniers contrats de travail de notre futur collègue. +- Une copie de son passeport +- Copie de son permis de conduire +- et une copie de son diplôme +- son CV détaillé +- la copie de l'offre publié sur Pôle Emploi, sa date de publication initiale et le nombre de renouvellements. +- La copie de son futur contrat de travail français, mentionnant CDI le détail des points 4.0 sur papier libre, à savoir : @@ -58,43 +57,43 @@ le détail des points 4.0 sur papier libre, à savoir : **Ce dossier complet a été envoyé à l'administration (DIRECCTE UT38) le 9 mai 2017.** - _L'embauche d'un salarié étranger n'est pas gratuite. Sachez que celle-ci vous coûtera environ 55% de son salaire mensuel brut en taxe._ +_L'embauche d'un salarié étranger n'est pas gratuite. Sachez que celle-ci vous coûtera environ 55% de son salaire mensuel brut en taxe._ - _Le salarié devra également s'acquitter de frais de procédures de 250€ à payer en timbre fiscal. Cette somme est dûe dans le mois de son arrivée en France._ +_Le salarié devra également s'acquitter de frais de procédures de 250€ à payer en timbre fiscal. Cette somme est dûe dans le mois de son arrivée en France._ - Après de (très) nombreux appels à la DIRECCTE UT38; service « introduction de main d'oeuvre étrangère », nous avons obtenu un **accord** de la part des services de l'Etat **le 29 juin 2017**. Il faut habituellement compter 1 à 2 mois pour obtenir un accord de l'administration. +Après de (très) nombreux appels à la DIRECCTE UT38; service « introduction de main d'oeuvre étrangère », nous avons obtenu un **accord** de la part des services de l'Etat **le 29 juin 2017**. Il faut habituellement compter 1 à 2 mois pour obtenir un accord de l'administration. - Cet accord se traduit par l'envoi du dossier complet de notre futur salarié à l'attention du consulat général de France dans son pays d'origine (En l'occurrence, consulat français à Sydney). Il y sera ensuite convoqué pour un entretien pour délivrance de visa. +Cet accord se traduit par l'envoi du dossier complet de notre futur salarié à l'attention du consulat général de France dans son pays d'origine (En l'occurrence, consulat français à Sydney). Il y sera ensuite convoqué pour un entretien pour délivrance de visa. - Par ailleurs, son dossier est ensuite transmis à l'OFII ([Office Français de l'Immigration et de l'Intégration](http://www.ofii.fr/)) qui est en charge de la suite de la procédure depuis la France. +Par ailleurs, son dossier est ensuite transmis à l'OFII ([Office Français de l'Immigration et de l'Intégration](http://www.ofii.fr/)) qui est en charge de la suite de la procédure depuis la France. - Notre futur collègue Haze a donc été convoqué pour un entretien **début août 2017** au Consulat, où il a du fournir l'ensemble de ses papiers d'identité, ainsi que le cerfa 15187*02 original et visé par la DIRECCTE. Il y a rempli le « LONG-STAY VISA APPLICATION FORM » [https://www.diplomatie.gouv.fr/IMG/pdf/Long_Stay_Visa_Application_Form-2.pdf](https://www.diplomatie.gouv.fr/IMG/pdf/Long_Stay_Visa_Application_Form-2.pdf) +Notre futur collègue Haze a donc été convoqué pour un entretien **début août 2017** au Consulat, où il a du fournir l'ensemble de ses papiers d'identité, ainsi que le cerfa 15187\*02 original et visé par la DIRECCTE. Il y a rempli le « LONG-STAY VISA APPLICATION FORM » [https://www.diplomatie.gouv.fr/IMG/pdf/Long_Stay_Visa_Application_Form-2.pdf](https://www.diplomatie.gouv.fr/IMG/pdf/Long_Stay_Visa_Application_Form-2.pdf) - Dix jours plus tard, il obtenait son accord d'entrée sur le territoire français, moyennant 99€ au service immigration/visa de l'aéroport et démarrait son contrat de travail le **4 septembre 2017 !** +Dix jours plus tard, il obtenait son accord d'entrée sur le territoire français, moyennant 99€ au service immigration/visa de l'aéroport et démarrait son contrat de travail le **4 septembre 2017 !** - ![](/img/2017-11-20171031_144322-min-300x225.jpg) +![](/img/2017-11-20171031_144322-min-300x225.jpg) - Mais la procédure n'est pas terminée 🙂 +Mais la procédure n'est pas terminée 🙂 - Arrivé en France, il a pris contact avec les services de l'OFII à Grenoble, afin de valider son visa long séjour valant titre de séjour et autorisation de travail. +Arrivé en France, il a pris contact avec les services de l'OFII à Grenoble, afin de valider son visa long séjour valant titre de séjour et autorisation de travail. - Il a été convoqué le 13 octobre 2017, afin de : +Il a été convoqué le 13 octobre 2017, afin de : - - Valider une visite médicale avec un médecin OFII, à laquelle il devait amener l'ensemble de ses antécédents médicaux (carnet de vaccination, ordonnances, compte-rendus d'hospitalisation récents, etc.) ainsi qu'une radio pulmonaire. Celle-ci devait être effectuée au centre anti-tuberculeux dans les 15 jours avant ce RDV. +- Valider une visite médicale avec un médecin OFII, à laquelle il devait amener l'ensemble de ses antécédents médicaux (carnet de vaccination, ordonnances, compte-rendus d'hospitalisation récents, etc.) ainsi qu'une radio pulmonaire. Celle-ci devait être effectuée au centre anti-tuberculeux dans les 15 jours avant ce RDV. - - Passer un test de langue française, écrit et oral, à l'issue duquel une note sur 25 lui sera attribuée. Celle-ci conditionnera le nombre d'heures de cours de français qu'il devra réaliser pour conserver le bénéfice de son visa. +- Passer un test de langue française, écrit et oral, à l'issue duquel une note sur 25 lui sera attribuée. Celle-ci conditionnera le nombre d'heures de cours de français qu'il devra réaliser pour conserver le bénéfice de son visa. - - Faire un « entretien d'accueil » avec un agent OFII, pour échanger sur ses motivations, et sur toute question qu'il se poserait à son arrivée en France. +- Faire un « entretien d'accueil » avec un agent OFII, pour échanger sur ses motivations, et sur toute question qu'il se poserait à son arrivée en France. - - Signer un « contrat d'intégration républicaine » +- Signer un « contrat d'intégration républicaine » - ![](/img/2017-11-cir-min-768x1024.jpg) +![](/img/2017-11-cir-min-768x1024.jpg) - Il obtient alors le visa « OFII » qui vaut titre de long séjour. +Il obtient alors le visa « OFII » qui vaut titre de long séjour. - ![](/img/2017-11-20171031_144455-min-300x225.jpg) +![](/img/2017-11-20171031_144455-min-300x225.jpg) - Avec sa bonne note de 19/25, Haze devra suivre 100 heures de cours de français, ce qui est le minimum requis pour valider le visa. Ce nombre d'heures peut être porté à 400h. +Avec sa bonne note de 19/25, Haze devra suivre 100 heures de cours de français, ce qui est le minimum requis pour valider le visa. Ce nombre d'heures peut être porté à 400h. Elles démarreront mi novembre, à raison de 2x3h par semaine. @@ -104,8 +103,8 @@ A l'issue de l'ensemble de la procédure, son visa long séjour sera validé, po **En conclusion ?** -* **Une procédure entamée « from scratch » en mars 2017, qui aura pris 9 mois au total. 5 mois avant son entrée officielle en France pour l'ensemble de la procédure d'autorisation, puis 4 autres mois de formalités « autres » permettant de valider son droit à vivre et travailler ici.** -* **Une grosse soixantaine d'heures passées (je n'y connaissait absolument rien). Comprendre la procédure, rassembler l'ensemble des pièces, relancer, et expliquer le tout en anglais à Haze 🙂** +- **Une procédure entamée « from scratch » en mars 2017, qui aura pris 9 mois au total. 5 mois avant son entrée officielle en France pour l'ensemble de la procédure d'autorisation, puis 4 autres mois de formalités « autres » permettant de valider son droit à vivre et travailler ici.** +- **Une grosse soixantaine d'heures passées (je n'y connaissait absolument rien). Comprendre la procédure, rassembler l'ensemble des pièces, relancer, et expliquer le tout en anglais à Haze 🙂** Et vous ? Avez-vous déjà recruté un salarié étranger ? Quelle procédure avez-vous suivie ? diff --git a/site/content/posts/2017-12-11-feature-flags-at-squarescale.md b/blog/_posts/2017-12-11-feature-flags-at-squarescale.md similarity index 91% rename from site/content/posts/2017-12-11-feature-flags-at-squarescale.md rename to blog/_posts/2017-12-11-feature-flags-at-squarescale.md index 1134501c4..6148d64a5 100644 --- a/site/content/posts/2017-12-11-feature-flags-at-squarescale.md +++ b/blog/_posts/2017-12-11-feature-flags-at-squarescale.md @@ -1,12 +1,12 @@ --- -title: "Privé : Feature flags at SquareScale" +title: 'Privé : Feature flags at SquareScale' author: Adrien date: 2017-12-11T15:20:42+00:00 draft: true private: true image: /img/2017-12-flipper.png categories: - - DÉVELOPPEMENT + - Développement logiciel tags: - feature flag - flipper @@ -14,8 +14,8 @@ tags: - ruby on rails - sogilis - squarescale - --- + # Feature flags at SquareScale {#feature-flags-at-squarescale} We started to talk about feature flags a while ago at SquareScale but never had the time to introduce themin our web application. None of us had the chance to work with feature flags in a previous application so we did not know very much about how to implement them, how to manage them in the application lifetime, and the most important part is that we didn't have any feedbacks about feature flags being used in production. That being said, we of course read about it. We especially read Martin Fowler's article: [Feature Toggles (aka Feature Flags)][1]. There are many reasons we need feature flags. @@ -38,21 +38,21 @@ I will not dig into what Flipper offers because you can read everything on their The setup is really easy. We just needed to add three gems. One is for flipper, two is for the UI, three is for the active record adapter. -{{< highlight bash >}} +```bash gem 'flipper' gem 'flipper-ui' gem 'flipper-active_record' -{{< /highlight >}} +``` A rails command allows us to generate the migrations, creating two tables. -{{< highlight bash >}} +```bash rails g flipper:active_record -{{< /highlight >}} +``` A bit of configuration into `config/initializers/flipper.rb`. -{{< highlight ruby >}} +```ruby require 'flipper/adapters/active_record' Flipper.configure do |config| @@ -61,13 +61,13 @@ Flipper.configure do |config| Flipper.new(adapter) end end -{{< /highlight >}} +``` Adding the route for the UI. -{{< highlight ruby >}} +```ruby mount Flipper::UI.app(Flipper) => '/flipper' -{{< /highlight >}} +``` And that's it. You're all done! @@ -75,12 +75,12 @@ And that's it. You're all done! As I said, we wanted to use Flipper to allow access to our new features to a group of users. The documentation shows how to do it: -{{< highlight ruby >}} +```ruby # this registers a group Flipper.register(:admins) do |actor| actor.respond_to?(:admin?) && actor.admin? end -{{< /highlight >}} +``` Here, an actor is anything having a `flipper_id` method. @@ -88,12 +88,12 @@ In our case our actors will be instances of `User`. We simply had to add -{{< highlight ruby >}} +```ruby Contract None => String def flipper_id "User;#{id}" end -{{< /highlight >}} +``` (I know we use contracts in Ruby, stay tuned for more information) @@ -101,7 +101,7 @@ The thing in that example is that you have to introduce a boolean into your acto We don't want to pollute our user class with a lot of booleans. We chose to create a `FlipperMembership` class containing all those booleans for a `User` with a `:has_one` relation. -{{< highlight ruby >}} +```ruby class CreateFlipperMembership < ActiveRecord::Migration[5.0] def change create_table :flipper_memberships do |t| @@ -114,38 +114,38 @@ class CreateFlipperMembership < ActiveRecord::Migration[5.0] end end end -{{< /highlight >}} +``` -{{< highlight rb >}} +```rb class User < ApplicationRecord has_one :flipper_membership end -{{< /highlight >}} +``` And then to add a `is?` method on the `User` class ([thanks for the idea][5]). -{{< highlight rb >}} +```rb Contract Symbol => Bool def is?(group) flipper_membership.send(group) end -{{< /highlight >}} +``` This way we can declare any Flipper group without forgetting to update our `FlipperMembership` model. -{{< highlight rb >}} +```rb Flipper.register(:db_choices_testers) do |actor| actor.respond_to?(:is?) && actor.is?(:new_feature_1_tester) end -{{< /highlight >}} +``` Now we just have to protect our code with Flipper: -{{< highlight rb >}} +```rb if Flipper.enabled? :new_feature_1, current_user ... end -{{< /highlight >}} +``` And that's all for now folks. It is really new for us at the moment, that's why we have no feedback to give yet (except it is really easy to set up). We will certainly write an other blog post about that in the coming months, stay tuned! @@ -157,7 +157,6 @@ Special thanks to Haze for his feedback. [1]: https://martinfowler.com/articles/feature-toggles.html [2]: https://blog.sogilis.com/posts/2017-11-07-end-to-end-testing-chrome-headless-squarescale/ - [3]: https://github.com/fetlife/rollout [4]: https://github.com/jnunemaker/flipper [5]: https://stackoverflow.com/questions/25712621/cant-get-flipper-feature-to-enable-for-a-group diff --git a/site/content/posts/2018-01-08-creation-dun-backlog-centre-utilisateurs.md b/blog/_posts/2018-01-08-creation-dun-backlog-centre-utilisateurs.md similarity index 96% rename from site/content/posts/2018-01-08-creation-dun-backlog-centre-utilisateurs.md rename to blog/_posts/2018-01-08-creation-dun-backlog-centre-utilisateurs.md index 29fdf0bb3..709dfc752 100644 --- a/site/content/posts/2018-01-08-creation-dun-backlog-centre-utilisateurs.md +++ b/blog/_posts/2018-01-08-creation-dun-backlog-centre-utilisateurs.md @@ -3,13 +3,13 @@ title: "Création d'un backlog centré utilisateurs : l'utilisation des UX Cards author: Marion Georges date: 2018-01-08T15:59:03+00:00 categories: - - ERGONOMIE + - Ergonomie tags: - ergonomie - expérience - méthodologie - --- + # Comment l'utilisation des UX cards lors d'un focus group, nous a permis de créer un backlog ? ## Introduction @@ -34,8 +34,8 @@ Notre séance de co-design devait donc répondre à la question suivante : quel 7 participants étaient présents : -* 6 utilisateurs d’entreprises de professions et secteurs d’activité différents -* moi-même, ergonome, qui animait la séance +- 6 utilisateurs d’entreprises de professions et secteurs d’activité différents +- moi-même, ergonome, qui animait la séance Nous étions dans une grande salle de réunion qui permettait d’avoir 3 espaces de réflexion distincts. @@ -81,6 +81,6 @@ De mon côté, un point est à améliorer concernant l’animation : l’enregis Par contre, cette séance m’a permis de proposer une maquette plus aboutie à mon client, avec des données réelles. Par conséquent, le client n'a demandé pratiquement aucune correction, ce que je trouve satisfaisant. -Marion Georges - [@mgeorges_](https://twitter.com/mgeorges_) +Marion Georges - [@mgeorges\_](https://twitter.com/mgeorges_) [1]: https://uxmind.eu/portfolio/ux-design-and-evaluation-cards/ diff --git a/site/content/posts/2018-03-01-distributed-reactive-loop-resiliency-framework.md b/blog/_posts/2018-03-01-distributed-reactive-loop-resiliency-framework.md similarity index 95% rename from site/content/posts/2018-03-01-distributed-reactive-loop-resiliency-framework.md rename to blog/_posts/2018-03-01-distributed-reactive-loop-resiliency-framework.md index 7bfb08566..558d157c2 100644 --- a/site/content/posts/2018-03-01-distributed-reactive-loop-resiliency-framework.md +++ b/blog/_posts/2018-03-01-distributed-reactive-loop-resiliency-framework.md @@ -1,18 +1,18 @@ --- -title: "The Distributed Reactive Loop: Handling Resiliency with a Simple Thought Framework" +title: 'The Distributed Reactive Loop: Handling Resiliency with a Simple Thought Framework' author: Simon Denier date: 2018-03-01T09:21:28+00:00 image: /img/2018-02-full_fsm.png categories: - - DÉVELOPPEMENT + - Développement logiciel tags: - 'Distributed' - 'distributedsystems' - 'ReactiveLoop' - 'system' - 3Dprint - --- + Once upon a time, we were tasked with designing a distributed system for 3D printers across the internet - something like a pool of workers for processing jobs in parallel. At the time we knew little of the customaries of 3D printing: how it works, how it fails, what kind of communication to expect. After the initial probe & discovery stage, it became obvious we had to design a distributed system in a not so common way. Because 3D printers are dealing with a physical process, taking some time to complete and requiring manual operation, we went back to basics and focused on two properties: keeping things consistent locally and making the overall system resilient in the face of inconsistency. This is the story of how we designed our system to handle such properties. ## 1 @@ -23,17 +23,17 @@ From a faraway point of view, distributed 3D printing is not different from the But when tackling the customaries of 3D printing, we quickly had to face the following evidence. -* 3D jobs take a long time, very long in term of computer time: from a few minutes for the smallest items to well over a day for big ones, with many regular objects taking at least a few hours. -* 3D printers are still a new thing, fragile machineries, with failures happening from time to time. -* Job failures can and will happen, and they can be costly: you lose hours of printer time, and you have to clean up the mess before taking a new job. -* Even after a successful job, a human operator is required to extract the item before the next job can be taken. +- 3D jobs take a long time, very long in term of computer time: from a few minutes for the smallest items to well over a day for big ones, with many regular objects taking at least a few hours. +- 3D printers are still a new thing, fragile machineries, with failures happening from time to time. +- Job failures can and will happen, and they can be costly: you lose hours of printer time, and you have to clean up the mess before taking a new job. +- Even after a successful job, a human operator is required to extract the item before the next job can be taken. This is different from what we often expect when it comes to job processing in a distributed context: fast jobs, cheap processing, « cost-free » mistakes, automatic retry, and a pool of standard, virtual workers, running without manual intervention. We had to design a system for long-running jobs, which would closely mirror what was going on, so that no precious processing time is lost because of inaccurate reporting and nothing harmful could happen. This means, for example: -* ensuring we had -eventually- an accurate status of whether the job has succeeded or not; -* ensuring the printer would not take another job while the previous item was still in its enclosure (obviously, a full-speed encounter between the printer head and a previous piece of hard plastic could result in some catastrophic damage). +- ensuring we had -eventually- an accurate status of whether the job has succeeded or not; +- ensuring the printer would not take another job while the previous item was still in its enclosure (obviously, a full-speed encounter between the printer head and a previous piece of hard plastic could result in some catastrophic damage). ## 2 @@ -41,7 +41,7 @@ When thinking about distributed systems, developers love to throw together a bun But given the above reasons, it was obvious that our main challenge would not be availability or scalability, but rather resiliency to fault in the system: job failure, but also communication failure, could happen any time, for example when a connection would be lost and a printer could not receive or send messages to the system. -There have been many warnings over the last years, even « horror stories », that micro-services systems are not _easier_ to design and understand, because you have to handle the cost of communication. From a graph point of view, you have to think about the _n_ nodes of your system, but also about the _m_ ways nodes communicate between them (with _m_ somewhere between _n-1_ and _n*(n-1)/2_). In other words, the more you add micro-services, the more you multiply how they can interact and fail with each other. +There have been many warnings over the last years, even « horror stories », that micro-services systems are not *easier* to design and understand, because you have to handle the cost of communication. From a graph point of view, you have to think about the *n* nodes of your system, but also about the *m* ways nodes communicate between them (with *m* somewhere between *n-1* and *n\*(n-1)/2*). In other words, the more you add micro-services, the more you multiply how they can interact and fail with each other. ![](https://github.com/sdenier/Articles-Sogilis/raw/master/distributed_reactive_loop/figures/nodes_connections.png) @@ -123,7 +123,6 @@ Failure can happen anytime during a printing process: the model might be faulty, The process is then similar to the « waiting retrieval » state. The operator cleans up the mess, checks the printer is operational, then signals the job as « recovering » for the supervisor. At the next roundtrip, when the printer sends the error state and the supervisor sees the job as recovering, it can send the recover signal to the printer. This signal puts the printer back in ready state, which can then start over. - ## 13 Distributed systems are full of loopholes and it is easy to make mistakes. This article primarily focus on the foundations of the reactive loop, driven by simplicity and ease of reasoning. However, other thoughts were in our mind when developing this project. This section intends to offer complementary points of view about the properties and limitations of the reactive loop design. @@ -142,16 +141,16 @@ Basically, the reactive loop framework favors some functional mindset/command-qu Care must be taken when designing printer FSM and transitions. Basically, we have two classes of printer state: -* either an « active » state (for example, printing), from which the printer will transition automatically when need be (and during which it does not accept supervisor commands). -* or a « blocking » state (for example, « waiting for retrieval »), from which the printer can not transition until the right supervisor command is received. +- either an « active » state (for example, printing), from which the printer will transition automatically when need be (and during which it does not accept supervisor commands). +- or a « blocking » state (for example, « waiting for retrieval »), from which the printer can not transition until the right supervisor command is received. In other words, in the current design, the supervisor only sends a command when the printer is in a blocking state. There is no nominal case where an « active » state change happens while the supervisor sends a command related to the previous state. It could still happen for example if the printer would go into error state (then, see the description for the error case). Overall, this mindset greatly reduces cases where printer could receive inconsistent messages. This also impacts the design of item status on the supervisor/database side, which should represent just what happened with « micro-statuses ». A good example of this is the « waiting for retrieval » state: -* when the supervisor receives the update for this state, it flags the item as being ready for retrieval. In this case, the supervisor can do nothing more and does not send any command to the printer. -* once an operator has effectively notified the supervisor that the item is retrieved, the item status is flagged as done. -* next time the supervisor and the printer perform a loop run (which happens immediately after the above action if they are connected), the combination of « item done » + « waiting for retrieval » status triggers the supervisor to send the command « done », which enables the transition back to the ready state. +- when the supervisor receives the update for this state, it flags the item as being ready for retrieval. In this case, the supervisor can do nothing more and does not send any command to the printer. +- once an operator has effectively notified the supervisor that the item is retrieved, the item status is flagged as done. +- next time the supervisor and the printer perform a loop run (which happens immediately after the above action if they are connected), the combination of « item done » + « waiting for retrieval » status triggers the supervisor to send the command « done », which enables the transition back to the ready state. Here, an event-driven approach works best to model the status through which an item goes. It prevents the model from becoming inconsistent, by having micro-steps which have a single source of transition. diff --git a/site/content/posts/2018-06-05-matinales-techniques-securiser-mise-en-production-logicielle-le-26-juin.md b/blog/_posts/2018-06-05-matinales-techniques-securiser-mise-en-production-logicielle-le-26-juin.md similarity index 79% rename from site/content/posts/2018-06-05-matinales-techniques-securiser-mise-en-production-logicielle-le-26-juin.md rename to blog/_posts/2018-06-05-matinales-techniques-securiser-mise-en-production-logicielle-le-26-juin.md index d0ed0cad4..93122fa75 100644 --- a/site/content/posts/2018-06-05-matinales-techniques-securiser-mise-en-production-logicielle-le-26-juin.md +++ b/blog/_posts/2018-06-05-matinales-techniques-securiser-mise-en-production-logicielle-le-26-juin.md @@ -1,10 +1,10 @@ --- -title: "[Matinales techniques] PME du numérique : (re) découvrez les bonnes pratiques pour sécuriser votre mise en production logicielle le 26 juin" +title: '[Matinales techniques] PME du numérique : (re) découvrez les bonnes pratiques pour sécuriser votre mise en production logicielle le 26 juin' author: Tiphaine date: 2018-06-05T14:35:58+00:00 image: /img/2018-06-illustration-prod.jpg categories: - - NOTRE VISION + - Notre vision tags: - 'efficience' - 'procedure' @@ -12,18 +12,18 @@ tags: - 'software' - 'testing' - deployment - --- + # Quelle qu’en soit la fréquence, la livraison de nouvelles fonctionnalités à vos clients est souvent un moment stressant : Une mauvaise maîtrise de cette étape clé peut avoir des répercussions directes sur votre activité. ## Les problèmes que nous voyons souvent : -* Vos procédures sont longues, délicates et coûteuses ? Ou pire, vous n’avez pas de process ? -* Vous aimeriez livrer de nouvelles fonctionnalités à vos clients plus fréquemment, plus régulièrement ? -* Seul un petit nombre de vos développeurs sait déployer en prod, ou pire, une seule personne ? -* Votre mise en production est génératrice de stress pour vos équipes, et cause parfois même des interruptions de service pour vos clients ? -* Vous aimeriez tester vos évolutions en conditions réelles sans risquer d’incident majeur, faire une migration graduelle de vos utilisateurs en production ? -* Vous souhaitez une infrastructure de niveau industriel mais vous n’avez pas les moyens d’avoir un ops/sys admin à plein temps ? +- Vos procédures sont longues, délicates et coûteuses ? Ou pire, vous n’avez pas de process ? +- Vous aimeriez livrer de nouvelles fonctionnalités à vos clients plus fréquemment, plus régulièrement ? +- Seul un petit nombre de vos développeurs sait déployer en prod, ou pire, une seule personne ? +- Votre mise en production est génératrice de stress pour vos équipes, et cause parfois même des interruptions de service pour vos clients ? +- Vous aimeriez tester vos évolutions en conditions réelles sans risquer d’incident majeur, faire une migration graduelle de vos utilisateurs en production ? +- Vous souhaitez une infrastructure de niveau industriel mais vous n’avez pas les moyens d’avoir un ops/sys admin à plein temps ? Sogilis vous propose un atelier mêlant présentation des concepts, démonstrations et cas d’usages illustrés pour que vous puissiez mieux gérer votre déploiement. diff --git a/site/content/posts/2018-06-06-matinales-techniques-deep-learning-controle-visuel-production-27-juin.md b/blog/_posts/2018-06-06-matinales-techniques-deep-learning-controle-visuel-production-27-juin.md similarity index 99% rename from site/content/posts/2018-06-06-matinales-techniques-deep-learning-controle-visuel-production-27-juin.md rename to blog/_posts/2018-06-06-matinales-techniques-deep-learning-controle-visuel-production-27-juin.md index 0b4b0450b..517c8f94c 100644 --- a/site/content/posts/2018-06-06-matinales-techniques-deep-learning-controle-visuel-production-27-juin.md +++ b/blog/_posts/2018-06-06-matinales-techniques-deep-learning-controle-visuel-production-27-juin.md @@ -4,15 +4,15 @@ author: Tiphaine date: 2018-06-06T10:26:26+00:00 image: /img/2018-06-CNN.jpg categories: - - NOTRE VISION + - Notre vision tags: - 'production' - cnn - deeplearning - machinelearning - tracking - --- + # Découvrez la méthode du deep learning avec Sogilis. ## Notre expertise historique  dans les problématiques de vision assistée par ordinateur, couplée aux nouvelles techniques de deep learning représente un avantage dans le contrôle, à différents niveaux, des étapes-clés de la chaîne de production. @@ -54,4 +54,3 @@ Précédemment employé en machine learning, il a rejoint Sogilis il y a plus d' Depuis 3 ans à Sogilis, il a essentiellement travaillé sur des problématiques de vision par ordinateur et de learning autour du tracking visuel pour les drones volants et sous-marins (Hexo+ et iBubble). Plus récemment en imagerie multispectrale pour l'agronomie. La vision par ordinateur étant de plus en plus liée au deep learning, l'évolution vers ce domaine en était une suite logique. **Modalités pratiques :** de 9h30 à 11h30, le mercredi 27 juin 2018 dans les locaux de Sogilis, 4 avenue Doyen Louis Weil (Immeuble Le Pulsar, 6e étage) - diff --git a/site/content/posts/2018-06-12-les-methodes-agiles-au-service-de-la-gestion-dentreprise.md b/blog/_posts/2018-06-12-les-methodes-agiles-au-service-de-la-gestion-dentreprise.md similarity index 99% rename from site/content/posts/2018-06-12-les-methodes-agiles-au-service-de-la-gestion-dentreprise.md rename to blog/_posts/2018-06-12-les-methodes-agiles-au-service-de-la-gestion-dentreprise.md index 132207946..64d93263f 100644 --- a/site/content/posts/2018-06-12-les-methodes-agiles-au-service-de-la-gestion-dentreprise.md +++ b/blog/_posts/2018-06-12-les-methodes-agiles-au-service-de-la-gestion-dentreprise.md @@ -4,14 +4,14 @@ author: Sogilis date: 2018-06-12T08:11:26+00:00 image: /img/2018-06-Recompense-pommes.png categories: - - LA VIE À SOGILIS + - La vie à sogilis tags: - agile - amélioration continue - management - vie interne - --- + Nous vous proposons un retour d'expérience sur notre utilisation de la méthodologie Kanban pour co-gérer de manière agile une entreprise. # Co-gestion de l'entreprise @@ -41,6 +41,7 @@ Nous en avons conclu qu’il était temps d’itérer et de s’organiser autour # 2ème itération - organisation agile et gamification Nous nous sommes inspiré d'une méthode agile et avons réorganisé les actions dans un Kanban de 3 colonnes : + - à faire - en cours - fait diff --git a/site/content/posts/2018-06-19-conditions-de-travail-des-professionnels-du-design-dinterfaces-numeriques.md b/blog/_posts/2018-06-19-conditions-de-travail-des-professionnels-du-design-dinterfaces-numeriques.md similarity index 97% rename from site/content/posts/2018-06-19-conditions-de-travail-des-professionnels-du-design-dinterfaces-numeriques.md rename to blog/_posts/2018-06-19-conditions-de-travail-des-professionnels-du-design-dinterfaces-numeriques.md index 8863bcd07..890294540 100644 --- a/site/content/posts/2018-06-19-conditions-de-travail-des-professionnels-du-design-dinterfaces-numeriques.md +++ b/blog/_posts/2018-06-19-conditions-de-travail-des-professionnels-du-design-dinterfaces-numeriques.md @@ -4,16 +4,16 @@ author: Sogilis date: 2018-06-19T15:33:22+00:00 image: /img/2018-06-banière_questionnaire.png categories: - - DESIGN - - ERGONOMIE + - Design + - Ergonomie tags: - business - design - enquête - ergonomie - ux-design - --- + Nous avons réalisé une étude auprès des professionnels du design d’interfaces numériques travaillant pour les clients externes. Nous nous sommes intéressées plus particulièrement aux prestations proposées et aux manières de travailler selon le profil (ergo, ux, ui...). Malgré une centaine de réponses à certaines questions, il est impossible de généraliser les résultats à l’ensemble de la population des professionnels du design d’IHM travaillant pour des clients externes en France. Néanmoins, des tendances peuvent être observées et certaines d’entre elles nous ont d’ailleurs surpris. @@ -90,11 +90,10 @@ Quoi qu’il en soit, le marché semble conscient de la valeur de l’UX-design ![](/img/2018-06-Tarifs.jpg) -Résultat du croisement des TJM selon le titre que les répondants se donnent (42 réponses, * différence significative entre UX-designer et UI/UX-designer). Le nombre de données par groupe est indiqué entre parenthèses après le nom de chaque groupe. +Résultat du croisement des TJM selon le titre que les répondants se donnent (42 réponses, \* différence significative entre UX-designer et UI/UX-designer). Le nombre de données par groupe est indiqué entre parenthèses après le nom de chaque groupe. De notre côté, nous nous retrouvons dans la majorité des groupes. Nous venons des domaines de l’ergonomie et des sciences cognitives et sommes salariées d’une ESN avec 3-5 ans d’expérience. Nous travaillons seules ou en équipe surtout dans notre entreprise. Nous vendons surtout des entretiens, des maquettes et des tests utilisateurs, à un tarif entre 600 et 800€/jour selon les conditions. Néanmoins, nous divergeons sur deux points avec les répondants. Le titre que l’on se donne et la facturation : ergonomes plutôt qu’UX-designer, et la facturation car nous travaillons souvent au forfait alors que la plupart facture au jour réellement travaillé. Cette étude nous permet d’enrichir notre réflexion et nous encourage en particulier à davantage essayer de facturer au jours réellement travaillés. - ¹ Tests statistiques : Mann & Whitney (𝛂 = 0,05) diff --git a/site/content/posts/2018-06-26-tous-responsable-personne-responsable.md b/blog/_posts/2018-06-26-tous-responsable-personne-responsable.md similarity index 98% rename from site/content/posts/2018-06-26-tous-responsable-personne-responsable.md rename to blog/_posts/2018-06-26-tous-responsable-personne-responsable.md index 998b82940..a492a8f3d 100644 --- a/site/content/posts/2018-06-26-tous-responsable-personne-responsable.md +++ b/blog/_posts/2018-06-26-tous-responsable-personne-responsable.md @@ -4,12 +4,11 @@ author: Sogilis date: 2018-06-26T14:20:00+00:00 image: /img/2018-06-affiches_roles.jpg categories: - - LA VIE À SOGILIS + - La vie à sogilis tags: - amélioration continue - management - vie interne - --- On continue dans cet article à décrire l’organisation de la gestion de Sogilis Lyon. On a vu dans l'[article précédent][1] la façon dont avait évolué, de façon agile, notre système de suivi des actions et la gamification qui y était associée. Nous y avions survolé l’aspect de la responsabilité. En effet, ce sujet mérite un article à part entière selon nous. Dans une entreprise à organisation horizontale tout le monde est responsable de tout. Le risque majeur est que chacun ne s’estime responsable de rien. Une facture doit être réglée ? Mon voisin peut bien s’en charger. Il faut contacter le propriétaire des locaux à propos de la fuite d’eau ? J’ai mieux à faire maintenant, quelqu’un d’autre le fera surement. @@ -30,8 +29,7 @@ C’est un jeu permettant d'appréhender une partie de la méthodologie agile de Le casino game permet de prendre conscience que quelques règles extrêmement simples permettent de fluidifier formidablement une chaîne de production et ainsi de produire davantage de valeur, tout en réduisant l’en-cours. L’idée est de faire réfléchir l’équipe, à chaque itération, pour améliorer cette chaîne. - -![](/img/2018-06-Casino-game.png "Casino Game") +![](/img/2018-06-Casino-game.png 'Casino Game') Courbe bleue : nos bénéfices au cours des trois itérations. Courbe rouge : notre en-cours en fonction des itérations. À la fin du jeu, nous avons identifié deux règles à intégrer dans notre entreprise : @@ -41,14 +39,11 @@ Courbe bleue : nos bénéfices au cours des trois itérations. Courbe rouge : no La règle des deux actions a été facile à mettre en place. Chacun a choisi un tampon encreur pour le représenter visuellement sur les post-it portant les actions. Cela permet rapidement de voir si l’un ou l’une d’entre nous a plus de deux tâches dans la colonne “En-cours”. Auquel cas on repasse une de ces tâches dans la colonne “À faire”, ou bien quelqu’un se porte volontaire pour prendre une des trois tâches. Ce genre de système visuel permet à chacun de savoir qui travaille sur quoi et de garantir la répartition de la charge de travail. - -![](/img/2018-06-avatars.jpg "avatars") +![](/img/2018-06-avatars.jpg 'avatars') Nos avatars - Concernant le sujet de la prise d’initiative, nous continuons de le travailler au quotidien, notamment grâce à l’initiative poker. - ## Initiative poker L’initiative poker (disponible [ici](/img/2018-06-initiative_poker.pdf)) est une adaptation très proche, proposée par Augustin, du [Delegation Poker](https://management30.com/product/delegation-poker/) inventé par Jurgen Appelo. Le Delegation Poker est à destination des managers d’équipes. Ces derniers n’existant pas chez nous, nous l’avons remanié pour qu’il réponde à notre problématique de prise d’initiative individuelle. En effet, si nous devons tous nous réunir avant chaque prise de décision, l’entreprise sera paralysée. En revanche certaines décisions méritent une réflexion collective. L’initiative poker a pour ambition de nous aider à discerner ces différentes situations pour gagner en efficacité. @@ -63,10 +58,9 @@ Au début de chaque tour de jeu, un participant évoque une situation nécessita - Annoncer l’action que l’on compte faire - Initiative : on fait l’action directement sans en parler -![](/img/2018-06-initiative-poker.png "Initiative poker") +![](/img/2018-06-initiative-poker.png 'Initiative poker') L’échelle de valeur pour la prise de décision - Nous y avons joué une première fois pour tester le jeu et prendre la température de l’équipe sur le sujet. Voici certaines des situations proposées : - acheter un second écran, @@ -92,7 +86,7 @@ Pour résoudre ces questions nous avons identifié et créé 5 rôles au sein de - Le Voleur, comme de juste, est en charge des finances - Le Scribe, qui s’occupe de tout ce qui a trait à l’administratif -![](/img/2018-06-affiches_roles.jpg "Affiches roles") +![](/img/2018-06-affiches_roles.jpg 'Affiches roles') Les fiches décrivant les buts des responsables de chaque rôle @@ -113,6 +107,4 @@ Un des doutes que nous avons le plus souvent entendu à propos de l’entreprise Nous sommes de fait tous responsables. Responsables, identifiés individuellement, de petites actions quotidiennes et de rôles importants mensuellement. Responsables collectivement de veiller à ce qu’une hiérarchie implicite ne se mette pas en place, de veiller à ce que chacun puisse prendre des initiatives sans se sentir coupable en cas d’échec. Cela demande un effort, quotidien, mais nous pensons que les retombées en valent largement la peine. - [1]: https://blog.sogilis.com/posts/2018-06-12-les-methodes-agiles-au-service-de-la-gestion-dentreprise/ - diff --git a/site/content/posts/2018-07-23-retours-sur-la-conference-web2day-2018.md b/blog/_posts/2018-07-23-retours-sur-la-conference-web2day-2018.md similarity index 69% rename from site/content/posts/2018-07-23-retours-sur-la-conference-web2day-2018.md rename to blog/_posts/2018-07-23-retours-sur-la-conference-web2day-2018.md index e3e708add..82e40cfb5 100644 --- a/site/content/posts/2018-07-23-retours-sur-la-conference-web2day-2018.md +++ b/blog/_posts/2018-07-23-retours-sur-la-conference-web2day-2018.md @@ -4,7 +4,7 @@ author: willy@sogilis.com date: 2018-07-23T13:47:51+00:00 image: /img/2018-07-Web2Day-featured.jpg categories: - - NOTRE VISION + - Notre vision tags: - conférence - microservices @@ -14,19 +14,19 @@ tags: - tech - web - web2day - --- -# **Retour sur le Web2Day 2018, par Willy Malvault + +# \*\*Retour sur le Web2Day 2018, par Willy Malvault Le Web2Day c'est une conférence Nantaise internationale, qui a pour sujet... le Web, qui a réuni 6500 personnes et fête ses 10 ans en 2018. Lors de cette conférence, j'ai : - * assisté à de très bons talks (3 ou 4 de niveaux excellents, ce qui est énorme pour une conférence, du moins en ce qui me concerne). - * donné mon talk  « Alice au pays des micro-services » avec [Julien Giovaresco][1]. - * sympathisé avec un « principal advisor » et un « lead consultant » de chez [Thoughtworks][2] UK. - * découvert le richissime éco-système Web / Startup Nantais. Et découvert deux sociétés « libérées » : [Troopers][3] et [Les Tilleuls.][4] - * testé plein de formes de réalité virtuelle, bu quelques bières, mangé des plats bios/vegans et des burgers, joué un peu au baby-foot et découvert le jeu du palais vendéen. +- assisté à de très bons talks (3 ou 4 de niveaux excellents, ce qui est énorme pour une conférence, du moins en ce qui me concerne). +- donné mon talk  « Alice au pays des micro-services » avec [Julien Giovaresco][1]. +- sympathisé avec un « principal advisor » et un « lead consultant » de chez [Thoughtworks][2] UK. +- découvert le richissime éco-système Web / Startup Nantais. Et découvert deux sociétés « libérées » : [Troopers][3] et [Les Tilleuls.][4] +- testé plein de formes de réalité virtuelle, bu quelques bières, mangé des plats bios/vegans et des burgers, joué un peu au baby-foot et découvert le jeu du palais vendéen. - **Disclaimer :** J'ajoute qu'étant donné qu'il y avait en moyenne 5 sessions en parallèle, j'ai aussi raté des choses sûrement très bonnes. Tout ça pour dire que je vous invite à aller voir [la page Youtube du Web2Day][5], afin de vous faire une idée par vous même de la popularité de certaines sessions. +**Disclaimer :** J'ajoute qu'étant donné qu'il y avait en moyenne 5 sessions en parallèle, j'ai aussi raté des choses sûrement très bonnes. Tout ça pour dire que je vous invite à aller voir [la page Youtube du Web2Day][5], afin de vous faire une idée par vous même de la popularité de certaines sessions. ## Un Mot sur l'organisation et l'accueil des speakers @@ -38,13 +38,18 @@ Une anecdote sur  l'ouverture de la conf : un concert de musique électro ([Em Excellent talk sur la place de l'utopie dans l'avenir du numérique. Quoi de mieux qu'une approche philosophique pour prendre un peu de recul sur notre métier et ouvrir une conférence sur le Web. Un must see ! Spoiler : il ne faut pas plus de personnes que ce qu'il y a dans cette salle pour changer le monde ! -{{< youtube LarBMjJk4TY >}} + + + ## Mon top 1 : Impact Team – Piloter des équipes produit par l’impact Excellent retour d'expérience sur la réorganisation des équipes chez MeilleursAgents. Il faut vraiment voir tout le talk pour comprendre leur organisation (spoiler : ils sont contents de leurs résultats). Le principe en deux mots : ils gardent les éléments qu'ils trouvent pertinents dans les méthodes agiles traditionnelles (rétrospectives essentiellement), ils cassent les « feature teams » (regardez la vidéo pour comprendre pourquoi, mais c’est le principe des « impact teams »), ils donnent des objectifs business directement aux équipes produit (dev + PO + autres métiers nécessaires), établissent des mesures pour évaluer l'atteinte ou non des objectifs. Ils sont sur des périodes de rétrospectives de 6 semaines, pratiquent depuis 9 mois, ont appris à jeter du code et sont convaincus qu'ils arrivent à délivrer de la valeur à leur produit beaucoup plus vite, et de façon plus pertinente, avec les impact teams. -{{< youtube kPmyROdRBpk >}} + + + + ![][7] @@ -56,8 +61,8 @@ Excellent retour d'expérience sur la réorganisation des équipes chez Meilleur Un talk sur les microservices par James Birnie (i.e. un « lead en devenir » de chez Toughtworks, qui parle microservices avec Martin Fowler et James lewis). Pour moi ce talk est un peu particulier, pour deux raisons : - 1. le sujet est le même que mon propre talk, et il est donné par un speaker de chez Toughtworks (à qui on attribue un des premiers papiers sur les microservices) - 2. je connais le talk par cœur avant même de l'avoir vu. En effet, Julien et moi avons bu quelques bières avec James la veille, et nous avons causé microservices pendant quelques heures. +1. le sujet est le même que mon propre talk, et il est donné par un speaker de chez Toughtworks (à qui on attribue un des premiers papiers sur les microservices) +2. je connais le talk par cœur avant même de l'avoir vu. En effet, Julien et moi avons bu quelques bières avec James la veille, et nous avons causé microservices pendant quelques heures. Dans ce talk, James explique que les microservices amènent un lot de complexité conséquent, très souvent sous-estimé, de part leur nature distribuée notamment. Il insiste sur le fait que beaucoup trop de gens se lancent dans les microservices, sans s'être posé la question « Pourquoi a t-on besoin de microservices ? ». Il questionne alors la rentabilité de l'architecture microservices au sein des projets auxquels il a pu prendre part (une dizaine ?). Sa conclusion est franche : il est très rare qu'une architecture microservices soit réellement utile, et n'apporte pas un surcoût ou une « sur-complexité ». C'est à dire que la plupart des projets utilisant une architecture microservices aurait pu, selon lui, être réalisé plus simplement, sans architecture microservices. @@ -65,7 +70,10 @@ Outre le fait que nous avons bien sympathisé avec James, nos discussions sur le Nous allons recroiser James dans un futur proche, c'est sûr ! -{{< youtube 0wPRSGtqmzU >}} + + + + ## Mon top 3 : Agile Methods are Dangerous @@ -73,7 +81,10 @@ Commençons par un mot sur le speaker : Julian Holmes est « principal consultan Bref, un sacré bonhomme, mais parlons plutôt de son talk : « les méthodes agiles sont dangereuses ! ». Dans lequel il résume les difficultés classiques qu'il rencontre pour faire adopter l'agilité à ses clients, ainsi que quelques techniques qu'il adopte avec succès, comme bannir tous les mots classiques du vocabulaire de l'agilité , utiliser les frameworks sans donner leurs noms et plein de techniques pour « adapter » des conversations autour de la transition agile... -{{< youtube 1eRb0tiKRPc >}} + + + + ## Another World, another Web (blockchain) @@ -81,9 +92,10 @@ Un quick talk (20 minutes) sur des applis Web reposant à 100 % sur des technolo J'ai eu l'occasion d'échanger avec Damien Lecan le lendemain, un personnage très sympa. Je dois le relancer pour soumettre son talk au SnowCamp 🙂 -{{< youtube _0hO9uKDlok >}} - + + + ![][8] @@ -95,15 +107,19 @@ Interview intéressante de Frédéric Mazzella. Au programme : passage de covoit Cette interview permet de comprendre la culture et l’histoire de Blablacar,  qui doit beaucoup aux grèves SNCF ! -{{< youtube 1-2_pyUGdt4 >}} + + + + ## CSS custom properties Découverte pour moi des CSS Custom Properties. Bien utilisé, ça a l'air puissant. Vous trouverez une bonne introduction sur ce codepen :  , mais la vidéo vaut le coup aussi ! -{{< youtube Phiu7jmENiw >}} - + + + ![][9] @@ -113,7 +129,10 @@ Découverte pour moi des CSS Custom Properties. Bien utilisé, ça a l'air puiss Utiliser les outils docker pour construire ses propres OS (target cloud, hyperviseurs et bare-metal (ARM ready)), c'est possible avec [LinuxKit][10]. Un très bon talk pour tous les amateurs de Docker. -{{< youtube iZRlpoaRFog >}} + + + + ![][11] @@ -123,13 +142,19 @@ Utiliser les outils docker pour construire ses propres OS (target cloud, hypervi Très bonne introduction à Redux (plus que React) avec des exemples et un speaker qui maîtrise bien le sujet. Je dirais que ce talk est parfait pour des débutants React & Redux, ou pour ceux qui ne connaissent pas ces formidables technologies. -{{< youtube wMaJ8sCuZcg >}} + + + + ## Comment doper efficacement sa cybersécurité au renseignement ? Talk assez théorique, mais très intéressant sur les bonnes pratiques en matière de cybersécurité. Le talk insiste sur le renseignement (i.e. construire une base de données avec toutes les informations pertinentes pouvant servir à défendre une infrastructure : blacklist adresses IP et nom de domaines, mais aussi méthodes d'intrusion). -{{< youtube 0FVI02WZWq0 >}} + + + + ## Retour sur notre intervention : Alice au pays des micro-services @@ -137,7 +162,10 @@ Le talk a été plutôt bien reçu, il y avait du monde dans la salle (une centa J'ai eu pas mal de questions en off, sur les 3 jours de la conférence. Ce sont souvent des gens qui viennent me dire : « c’est marrant, le problème que tu décris ici, c’est exactement ce que l’on a chez nous ! ». Ou alors : « on a commencé à faire du microservices, on a choisi telle ou telle techno. Tu penses que ça marche ? ». Bref, on sent bien que le sujet des micro-services « titille » pas mal de monde. -{{< youtube xT-YIsLQdlw >}} + + + + [1]: http://julien.giovaresco.fr/ [2]: https://www.thoughtworks.com/ diff --git a/site/content/posts/2018-10-17-prendre-en-compte-le-besoin-utilisateur.md b/blog/_posts/2018-10-17-prendre-en-compte-le-besoin-utilisateur.md similarity index 85% rename from site/content/posts/2018-10-17-prendre-en-compte-le-besoin-utilisateur.md rename to blog/_posts/2018-10-17-prendre-en-compte-le-besoin-utilisateur.md index 8e2a5d618..3adf10ef3 100644 --- a/site/content/posts/2018-10-17-prendre-en-compte-le-besoin-utilisateur.md +++ b/blog/_posts/2018-10-17-prendre-en-compte-le-besoin-utilisateur.md @@ -3,8 +3,8 @@ title: Prendre en compte le besoin utilisateur, sans utilisateur. author: Sogilis date: 2018-10-17T14:05:55+00:00 categories: - - DESIGN - - ERGONOMIE + - Design + - Ergonomie tags: - business - design @@ -12,11 +12,11 @@ tags: - ergonomie - ux-design --- + La base du métier d’ergonome est de prendre en compte le besoin utilisateur et donc de les interroger. Récemment, j’ai dû travailler sur une problématique d’un client sans utilisateur. Le budget et le temps défini par mon client n’était pas extensible. Le client jugeait également le sujet peu complexe pour inclure des utilisateurs. J’ai ainsi eu l’occasion de travailler avec cette contrainte : ne pas échanger avec les utilisateurs de l’interface. J’analyse cette expérience afin d’essayer de confirmer ou non l’hypothèse selon laquelle la conception sans utilisateur est difficile et est un non-sens. - # On déroule quand même la démarche centrée utilisateur. ## Exploration : comprendre la demande et reformuler une problématique. @@ -25,29 +25,20 @@ Le sujet de travail portait sur une boîte de dialogue, faisant partie d’un lo La demande de mon client portait sur deux points particulièrement bloquants : +l’agencement des informations ne suivait pas la logique métier contraignant les utilisateurs à dessiner le process sur une feuille pour être sûrs de leur paramétrage, - l’agencement des informations ne suivait pas la logique métier contraignant les utilisateurs à dessiner le process sur une feuille pour être sûrs de leur paramétrage, - - - un vocabulaire non adapté faisant hésiter les utilisateurs sur le choix des champs à remplir. - - +un vocabulaire non adapté faisant hésiter les utilisateurs sur le choix des champs à remplir. J’ai proposé au client de commencer par un atelier de partage d’information, afin de prendre connaissance du sujet et de comprendre ce qui n’allait pas avec cette interface. Ce premier atelier permet d’explorer le sujet métier. Habituellement, cette phase inclut les utilisateurs afin d’identifier et de hiérarchiser les scénarios d’utilisation. Différents outils peuvent être utilisés pour récolter ces informations, comme des entretiens, des tests utilisateurs, des évaluations etc. A la fin, nous sommes capables d’avoir une vision globale et priorisée sur les axes à retravailler et à valoriser. Dans le cas présent, j’ai réalisé un entretien semi-directif avec le client. Cela m’a permi de récolter des informations sur ces trois sujets : +Le contexte métier de l’utilisation de cette interface : qui l’utilise ? dans quel but ? de quoi l’utilisateur a-t-il besoin pour remplir les champs ? Est-ce que cette interface fait partie d’une chaîne (besoin de mémoriser des informations pour l’écran suivant) ? - Le contexte métier de l’utilisation de cette interface : qui l’utilise ? dans quel but ? de quoi l’utilisateur a-t-il besoin pour remplir les champs ? Est-ce que cette interface fait partie d’une chaîne (besoin de mémoriser des informations pour l’écran suivant) ? - - - Le fonctionnement existant : sans cette interface comment les utilisateurs réalisent cette action ? que doit-elle faciliter ? - - - La problématique : quelles sont les difficultés d’utilisation, de compréhension, de visualisation exprimées par les utilisateurs ? +Le fonctionnement existant : sans cette interface comment les utilisateurs réalisent cette action ? que doit-elle faciliter ? +La problématique : quelles sont les difficultés d’utilisation, de compréhension, de visualisation exprimées par les utilisateurs ? A l’issu de l’entretien, j’ai pu identifier des axes d’améliorations à travailler, que j’ai validé avec le client. - ## Idéation et conception : recherche des solutions possibles. Je lui ai ensuite proposé de poursuivre par une phase d’idéation. Cette phase permet de rechercher différentes solutions. Pendant une heure, le client et moi avons utilisé un tableau blanc pour dessiner les solutions qui nous paraissaient répondre aux problèmes. Nous avons pris les problèmes un par un. @@ -56,22 +47,15 @@ Habituellement, je dois choisir de présenter une ou deux solutions au client et La séance réalisée avec le client était un peu différente, car au lieu de rechercher des solutions auprès des utilisateurs, la recherche s’est faite avec le client. J’ai en effet essayé de discuter de toutes les solutions possibles avec lui, pour récupérer le maximum d’informations sur ses blocages éventuels. L’avantage de cette séance fut la rapidité de l’itération. Au lieu des allers-et-retour sur plusieurs jours, tout s’est passé en 2h. A la fin de cet atelier, je pensais donc avoir une bonne vision de ce qui pourrait ou pas être adapté. - - La troisième étape fut une étape de conception où j’ai dû assembler les solutions possibles validées avec le client pour obtenir une interface utilisable et comprise par les utilisateurs. Je me suis vite rendu compte que je n’avais finalement pas toutes les informations pour créer cette interface. Je suis revenu vers le client avec une proposition de maquette afin de provoquer un nouvel échange sur les besoins métier. En général, je propose un seul aller-et-retour au client sur la maquette. Je préfère la confronter directement aux utilisateurs, via des tests utilisateurs, et sensibiliser le client à cette démarche itérative plutôt que de rester en circuit fermé. C’est un risque de se reposer seulement sur notre propre expérience pour valider l’utilisabilité d’une interface. La seule chose que nous réussirons à valider c’est que l’interface nous convient personnellement, mais j’y reviendrai plus tard. - - -J’ai finalement réussi à finaliser ma proposition sous forme de maquette cliquable. l’aspect cliquable aide à se projeter dans le comportement de l’interface. Même si les tests utilisateurs n’étaient pas prévus, ce travail de maquettage cliquable a eu pour objectif d’aider les développeurs à prendre en main le sujet et comprendre la solution envisagée.  _L’aspect cliquable est un sujet à double tranchant que j’aborde dans cet autre article _[lien]. - - +J’ai finalement réussi à finaliser ma proposition sous forme de maquette cliquable. l’aspect cliquable aide à se projeter dans le comportement de l’interface. Même si les tests utilisateurs n’étaient pas prévus, ce travail de maquettage cliquable a eu pour objectif d’aider les développeurs à prendre en main le sujet et comprendre la solution envisagée.  *L’aspect cliquable est un sujet à double tranchant que j’aborde dans cet autre article *[lien]. ## C'est difficile de se mettre à la place de l'utilisateur et d'y rester. Lors de ce travail, je me suis basée uniquement sur le point de vue de mon client. J’ai pu constater trois problèmes à cela : la déperdition d’information d’un intermédiaire, la vision technique du client et un avis unique qui n’est pas utilisateur. - - Lors de nos premiers échanges nous avons discuté des problèmes rencontrés par les utilisateurs. J’étais frustrée, car je n’avais aucun contrôle de la manière dont ont été récoltées ces informations. Le plus difficile lorsque l’on récolte ce genre d’informations c’est d’éviter les biais et obtenir une réponse exploitable. Ces biais peuvent apparaître dans la formulation des questions, dans le contexte d’utilisation ou par rapport aux liens existants entre le client et l’utilisateur par exemple. D’autre part, j’ai conscience qu’il y a une dégradation de l’information entre ce que dit un utilisateur, ce que le client a compris, ce qu’il a retenu et comment il me le restitue. +Lors de nos premiers échanges nous avons discuté des problèmes rencontrés par les utilisateurs. J’étais frustrée, car je n’avais aucun contrôle de la manière dont ont été récoltées ces informations. Le plus difficile lorsque l’on récolte ce genre d’informations c’est d’éviter les biais et obtenir une réponse exploitable. Ces biais peuvent apparaître dans la formulation des questions, dans le contexte d’utilisation ou par rapport aux liens existants entre le client et l’utilisateur par exemple. D’autre part, j’ai conscience qu’il y a une dégradation de l’information entre ce que dit un utilisateur, ce que le client a compris, ce qu’il a retenu et comment il me le restitue. Cette restitution est le second point qui coince quand l’interlocuteur unique est le client. Quand je lui parlais besoin utilisateur il me répondait solutiontechnique, quand je lui parlais scénario métier, il me répondait composant. La difficulté pour moi dans ce type d’échange est de discerner ce qui relève dubesoin métier, de ce qui relève de la solution technique mise en place par le client. C’est notamment pour éviter ce risque que la rencontre d’utilisateursest un besoin fort de notre métier. Pouvoir échanger avec au moins une personne qui utilise l’interface tous les jours pour ses besoins métier. Cela nouspermet de revenir à la base du besoin et d’identifier le pourquoi, socle de toute notre réflexion de conception. @@ -79,26 +63,18 @@ Cette restitution est le second point qui coince quand l’interlocuteur unique Enfin, le troisième point concerne le fait que j’avais un seul interlocuteur. Multiplier les interlocuteurs permet d’enrichir notre compréhension des choses.Le risque de discuter avec un interlocuteur unique, c’est d’avoir une seule version du problème, des besoins et des attentes. Pendant la phase de conception,j’ai ressenti que je tournais en rond dans ma réflexion. C’est en expliquant à ma collègue ergonome mes interrogations à propos de la maquette, que j’airéussi à débloquer ma réflexion. J’ai été chercher un autre point de vu, plus neuf pour m’aider à déceler ce qui bloquait ma réflexion et identifier leséléments que je n’avais pas anticipés. - - - ## Concevoir pour une personne es - Ce qui m’a manqué pendant cette conception sans utilisateur, c’est de parler du métier à l’état pur, sans considération pour la solution technique. Nous ne pouvons pas connaître tous les métiers du monde, ce serait impossible et pas vraiment souhaitable. Le travail de conception débute donc naturellement par la compréhension du métier. Quelles sont les tâches à réaliser, dans quel but, comment sont-elles réalisées, quels sont les problèmes parfois rencontrés, etc. Chaque individu suit un chemin différent pour son métier. Rencontrer différentes personnes permet donc d’identifier et de comprendre les chemins possibles, les raisons qui poussent à faire différemment, comprendre le contexte d’utilisation global. La rencontre d’un seul utilisateur via une seule méthode de récolte d’information n’est donc pas suffisante. En effet, en fonction des techniques, les utilisateurs ne livrent pas les mêmes informations. Les hypothèses de travail en seront renforcées. Enfin, rencontrer plusieurs personnes permet d’identifier plus facilement si les pratiques décrites sont communes ou plus particulières. Le point critique dans la compréhension du métier est de déterminer si la tâche est récurrente ou non, fait parti d’un scénario classique ou particulier et est une pratique commune ou particulière à un individu. Imaginons que l’on ne rencontre qu’un utilisateur. Il nous raconte comment il travaille, on trouve une solution. Le produit sort et les premiers utilisateurs remontent des difficultés non prévues par l’utilisateur premier. La conséquence c’est de devoir potentiellement modifier tout le design. Ce qui représente un coût énorme en temps et en argent. - L’exercice de l’entretien n’est pas facile. Il demande une préparation afin de récolter de l’information pertinente avant de s’engager dans la recherche de solution. Lorsque l’information de base est erronée, nous formulons une hypothèse fausse, trouver une solution qui ne résout pas le problème et au final l’utilisateur a des difficultés à utiliser la solution ou refuse carrément de l’utiliser. - ## Conclusion - - Finalement, j’ai accepté de revoir l’utilisabilité d’une interface sans m’appuyer sur les utilisateurs. Je me suis donc projetée dans des usages que je ne connais pas,. J’ai tâtonné, en me basant sur son expérience, mes propres usages. Je savais que ce serait difficile de prendre du recul sur l’interface que j’aurais conçue. Ca l’a été. Comment se rendre compte que cette formulation m’appartient et n’est pas forcément comprise par le grand nombre ? Pour pallier cela, j’ai effectué deux fois plus d’aller-et-retours avec mon client qu’habituellement. Nous ne sommes pas les utilisateurs de cette interface. Il est donc sécurisant de les intégrer dès le début de la conception afin de comprendre dès le départ les usages et anticiper les problèmes. diff --git a/site/content/posts/2019-01-11-object-mother-builder-java.md b/blog/_posts/2019-01-11-object-mother-builder-java.md similarity index 90% rename from site/content/posts/2019-01-11-object-mother-builder-java.md rename to blog/_posts/2019-01-11-object-mother-builder-java.md index 5c6edc462..84638f5ca 100644 --- a/site/content/posts/2019-01-11-object-mother-builder-java.md +++ b/blog/_posts/2019-01-11-object-mother-builder-java.md @@ -4,14 +4,14 @@ author: jean-baptiste@sogilis.com date: 2019-01-11T14:05:55+00:00 image: /img/2019-01-objectmother_builder_java.jpg categories: - - DÉVELOPPEMENT + - Développement logiciel tags: - builder - Java - objectmother - test - --- + Les patterns [Object Mother][1] et [Builder][2] apportent chacun leur lot d’avantages à nos tests automatisés, mais peuvent aussi être combinés pour aller plus loin dans la lisibilité et la maintenabilité de nos tests. Nous verrons dans cet article comment il est possible de réaliser cela. @@ -24,7 +24,7 @@ Si vous n’êtes pas familiers avec les patterns [Object Mother][1] et [Builder Object Mother permet de fournir des objets pré-configurés pour nos tests : -{{< highlight java >}} +```java public static HeroBuilder one() { return new HeroBuilder() .name("Leodagan") @@ -44,19 +44,19 @@ public static HeroBuilder merlin() { .allegiance(KingdomMother.logres().build()) .caste(WIZARD); } -{{< /highlight >}} +``` Le principe est le suivant : -* Chaque Object Mother fournit une factory de base avec une configuration par défaut : c’est la méthode `one()`. -* Toutes les autres factories utilisent cette factory de base. -* Dans cette versions, les Object Mother retournent des builders et non des instances. +- Chaque Object Mother fournit une factory de base avec une configuration par défaut : c’est la méthode `one()`. +- Toutes les autres factories utilisent cette factory de base. +- Dans cette versions, les Object Mother retournent des builders et non des instances. ## Avantages **(1)** Le pattern Object Mother permet d’améliorer la lisibilité des tests en retirant les constantes inutiles à la compréhension du test. Exemple : -{{< highlight java >}} +```java @Test @DisplayName("Can you see all information I have to write in order to build a single hero?") void without_mother_object() { @@ -81,14 +81,12 @@ void with_mother_object() { Assertions.assertThat(age) .isEqualTo(884); } -{{< /highlight >}} +``` L’exemple ici est plutôt trivial, mais imaginez cela dans une vraie application avec des classes ayant de nombreux attributs et/ou de nombreuses compositions. - **(2)** Lors d’une évolution future, si un nouvel attribut obligatoire est ajouté à une classe, alors il suffit de modifier la factory de base pour que tous les tests passent. En effet, la grande majorité des tests n’instancient plus d’objet eux-même, mais passent par une factory. La maintenance est ainsi facilitée (il devient inutile de modifier tous les tests, dans le scénario décrit ci-avant). - **(3)** L’utilisation de builders permet de customiser les objets générés par les Object Mother pour le besoin du test (`HeroMother.one().dateOfBirth(...).build()`). Sans cela, il faudrait multiplier les factories pour chaque besoin avec une méthode prenant en paramètre toutes les informations nécessaires au cas testé. On peut imaginer que cela nécessiterait une factory par test. ## Inconvénients et difficultés @@ -97,26 +95,24 @@ L’exemple ici est plutôt trivial, mais imaginez cela dans une vraie applicati [Lombok][3] peut potentiellement résoudre ce problème puisqu’il permet de générer automatiquement ces builders. - **(2)** La frontière entre méthode de factory et méthode de builder n’est pas toujours facile à identifier. Exemple avec trois possibilités pour créer une même configuration : -{{< highlight java >}} +```java HeroMother.king().build() HeroMother.one().king().build() HeroMother.one().caste(KING).build() -{{< /highlight >}} +``` **(3)** `.build()` est nécessaire partout pour générer l'objet final (pollution cognitive). - ## Ce qu’il faut retenir Combiné à des assertions custom [AssertJ][4], on arrive, avec cette technique, à des tests concis, qui restent lisibles et maintenables, même si on sent qu'il serait possible de faire mieux avec un langage plus évolué que **Java**, ce que nous verrons dans un prochain article avec [Kotlin][5]. ## Notes -* Ce principe est très largement inspiré par un [article de Rafał Borowiec][6]. -* Le projet dont sont issus les extraits de code est disponible [sur Github][7]. +- Ce principe est très largement inspiré par un [article de Rafał Borowiec][6]. +- Le projet dont sont issus les extraits de code est disponible [sur Github][7]. [1]: https://martinfowler.com/bliki/ObjectMother.html [2]: https://en.wikipedia.org/wiki/Builder_pattern diff --git a/site/content/posts/2019-12-13-data-test.md b/blog/_posts/2019-12-13-data-test.md similarity index 61% rename from site/content/posts/2019-12-13-data-test.md rename to blog/_posts/2019-12-13-data-test.md index b3163b745..a7086dcb4 100644 --- a/site/content/posts/2019-12-13-data-test.md +++ b/blog/_posts/2019-12-13-data-test.md @@ -4,7 +4,7 @@ author: alban@sogilis.com date: 2019-12-13 image: /img/2019-12-data-test.jpeg categories: - - DÉVELOPPEMENT + - Développement logiciel tags: - Js - VueJs @@ -12,43 +12,40 @@ tags: - test --- -*For the article the technical stack used is Vue.js front-end framework and Jest testing framework* +_For the article the technical stack used is Vue.js front-end framework and Jest testing framework_ ## Let’s introduce the problem by an example We have a button to go to the previous page. -{{< highlight html >}} -
- Back -
-{{< /highlight >}} +```html +
Back
+``` And the unit test: -{{< highlight js >}} +```js it('goes to the previous page when back button is clicked', () => { - // mock goToPreviousPage method - wrapper.vm.goToPreviousPage = jest.fn() + // mock goToPreviousPage method + wrapper.vm.goToPreviousPage = jest.fn(); - wrapper.find('.btn-arrow').trigger('click') + wrapper.find('.btn-arrow').trigger('click'); - expect(wrapper.vm.goToPreviousPage).toHaveBeenCalled() -}) -{{< /highlight >}} + expect(wrapper.vm.goToPreviousPage).toHaveBeenCalled(); +}); +``` -Now we want to change the design by replacing `.btn-arrow` to `.btn-link` css class. +Now we want to change the design by replacing `.btn-arrow` to `.btn-link` css class. The design can also change by adding tag like `div`, `a`, `span` etc.. -{{< highlight html >}} - -{{< /highlight >}} +```html + +``` But now, our test is red because the css selector is `undefined` while the functionality still works. -## Solution +## Solution + The solution to be resilient to this kind of changes is to introduce HTML `data-*` attributes in your DOM. Let’s take the World Wide Web Consortium (W3C) definition: @@ -57,13 +54,17 @@ Let’s take the World Wide Web Consortium (W3C) definition: We can add to our HTML a `data-test` attribute to test our DOM. -{{< highlight html >}} - - -{{< figure src="/img/article_bdd/2020_09_09_bdd_figure_1.png" title="Figure 1. Principaux facteurs d’échec dans le développement logiciel">}} - -Ces facteurs d’échec sont d’autant plus impactants et marquants dans un contexte de développement utilisant une approche à base de cycle en V. En effet, il s’agit d’un modèle de développement rigide ne permettant pas d’intégrer facilement des modifications et des adaptations aux exigences. Il s’agit également d’un modèle de développement favorisant une mauvaise compréhension des exigences, en raison de l’inexistence d’activités techniques permettant la convergence de la compréhension des besoins entre tous les acteurs du projet (l’équipe fonctionnelle, les utilisateurs et l’équipe de développement). D’après les études dans [EcoImp], plus de 65% des anomalies sont identifiées tard dans le cycle de vie de développement logiciel, pendant la phase d’implémentation et d’intégration. Une grande partie de ces anomalies est due à l’incohérence entre le code développé et les exigences définies. Aujourd’hui, des anomalies onéreuses en temps de traitement sont introduites tôt dans le cycle de développement logiciel. Elles résultent d’un manquement et de défaillances dans la capture des besoins et dans la gestion de leurs évolutivités, entre les équipes qui rédigent les exigences et les équipes qui implémentent et testent le logiciel. - -Ces facteurs d’échec et de dépassement de coûts pourraient être évités et atténués en ayant recours à des pratiques d’ingénieries et des méthodes de développement comme le BDD. En effet, la méthode de développement pilotée par le comportement (en anglais Behavior-Driven Development ou BDD) a été introduite pour fluidifier et simplifier la compréhension et la validation des besoins entre tous les acteurs qui interviennent dans le cycle de vie de développement du logiciel. Il s’agit d’une démarche où la définition des exigences d’un côté et des scénarios de tests d’un autre côté deviennent intrinsèquement liées. Le développement piloté par le comportement serait bénéfique et facile à appliquer avec les méthodes de développement itératives et incrémentales. - -La section II présente un état de l’art des plateformes de développement BDD existantes. La section III discute les avantages d’utiliser l’approche BDD pour améliorer la qualité et l’efficience des développements logiciels. Les sections IV et V expliquent comment une approche BDD peut être utilisée dans le développement et la vérification d’exigences fonctionnelles, y compris dans le cadre du développement de logiciels critiques et normés. La section VI présente le Framework XReq [XReq] et des exemples de cas d’utilisation. - -## II. Développement piloté par le comportement - -Le développement piloté par le comportement favorise l’utilisation d’un langage naturel et commun pour expliquer les exigences fonctionnelles à travers des scénarios de test. Les scénarios de tests permettent d’apporter des exemples concrets d’application des exigences. Le développement piloté par le comportement permet de s’assurer que les exigences logicielles répondent bien aux besoins utilisateurs. Le langage naturel Gherkin [Gherkin] est utilisé par la plupart des Framework BDD pour écrire les scénarios de tests. Il s’agit d’un langage simple et facile à comprendre et à utiliser pour décrire le déroulement d’une fonctionnalité. Il décompose un scénario de test en trois principales étapes : - -- Initialisation du Contexte (Given) -- Action (When) -- Résultat attenu (Then) - -Le scénario de test est parfois suivi par un jeu de données qui complète l’instanciation du scénario. - -{{< figure src="/img/article_bdd/2020_09_09_bdd_figure_2.png" title="Figure 2. Exemple d’un Scénario de test avec XReq [XReq]">}} - -La figure 2 illustre un exemple de scénario de test qui se déroule autant de fois que de nombre de lignes dans le jeu de données avec XReq. - -Des Frameworks de développement BDD existent comme Cucumber [Cucumber], XReq [Xreq], SpecFlow [Specflow], Jbehave [Jbehave] et Concordion [Concordion]. Cucumber est le Framework le plus connu, qui propose différentes implémentations pour de nombreux langages de programmation (Java, Go, Javascript, etc.). - -SpecFlow a été développé en s’inspirant de Cucumber et utilise le langage Gherkin dans la description des scénarios de tests. Il est dédié aux applications implémentées en .net C# et s’interface avec Visual Studio. Concordion [Concordion] est un Framework de BDD écrit en Java et intègre le Framework de tests unitaires JUnit. - -XReq [XReq] est un Framework de test dédié au développement de logiciels embarqués écrits en C/C++ ou en Ada. Avec XReq, il est possible de générer du code de test, en langage C et en Ada et de générer des rapports de tests, en natif ou en mode croisé (tests XReq exécutés sur la plateforme cible). XReq dispose d’un outil de reporting et se greffe facilement avec l’outil de reporting cucumber-report. - -{{< figure src="/img/article_bdd/2020_09_09_bdd_figure_3.png" title="Figure 3. Exemples de Frameworks BDD">}} - -La plupart des Frameworks BDD ne se contentent pas de l’aspect documentaire dans l’écriture des scénarios de tests, mais fournissent des outils pour générer le code des squelettes des tests à partir des scénarios. Ils fournissent également des moteurs d’exécution des tests afin de faciliter l’automatisation de l’exécution des scénarios de tests avec le jeu de données correspondant. La figure 4 illustre le cycle de vie d’un scénario de test dans les Frameworks BDD avancés [Cucumber] [Xreq] [SpecFlow] depuis l’écriture des scénarios des tests jusqu’à la génération du rapport des tests, en passant par la génération du code des tests, leurs implémentations et leurs exécutions. - -{{< figure src="/img/article_bdd/2020_09_09_bdd_figure_4.png" title="Figure 4. Cycle de vie d’un scénario de Test dans un Framework BDD">}} - -## III. Efficience et Qualité - -L’utilisation des techniques BDD dans le développement logiciel pourrait contribuer à l’amélioration de la qualité logicielle et à l’efficience des équipes de développement. Cela peut se traduire à plusieurs niveaux, pour plusieurs activités techniques : - -**Maîtriser le niveau de la qualité logicielle et améliorer la qualité des exigences** : la perte de la maîtrise de la qualité d’un logiciel est souvent liée à l’accumulation des anomalies. Ces anomalies sont souvent découvertes tard dans le cycle de vie de développement logiciel et deviennent compliquées à corriger. Elles deviennent coûteuses car les équipes de développement ne disposent pas de moyens suffisants, en termes de tests, pour vérifier la non régression du logiciel suite à la livraison d’un correctif. Une des causes racines de la perte ou la non maîtrise d’un logiciel est l’absence de tests logiciels automatiques couvrant les principales fonctionnalités. - -L’application d’une approche BDD durant la phase de capture de besoins permet d’écrire les scénarios de tests à implémenter dans un second temps. Les scénarios de tests écrits en BDD seront les tests de non régression qui vont aider à garantir un niveau de fiabilité du logiciel et à le maintenir plus facilement. - -D’un autre côté, nous savons qu’une grande partie des anomalies est due à une incompréhension des exigences. Ces anomalies sont introduites tôt dans le cycle de vie du logiciel. Pour réduire le nombre d’anomalies liées à une mauvaise compréhension des exigences, il faut recourir à des techniques BDD. En effet, les scénarios de tests BDD permettent de vérifier que les exigences ont été correctement écrites et comprises par les équipes de développement avant de procéder à leurs implémentations. Cette phase de vérification, au moyen de scénarios de tests BDD, peut amener à itérer et à améliorer les exigences tant au niveau fonctionnel qu’au niveau des besoin d’utilisabilité. - -**Améliorer la qualité des tests logiciels** : généralement, les tests sont écrits par les développeurs après l’implémentation des fonctionnalités. Les développeurs ont toujours tendance à écrire les tests selon leur compréhension des exigences et en fonction des développements qu’ils ont menés. De plus, il n’y a souvent pas d’activités techniques pour vérifier les tests écrits par les développeurs. Cela peut amener à disposer de tests incomplets, ou mal écrits, en face d’une implémentation incorrecte des exigences. Pourtant, les indicateurs de qualité seraient au vert puisque tous les tests passent et on peut disposer d’un taux élevé de couverture de code. - -Une approche BDD peut apporter des solutions à ce niveau en améliorant d’une manière considérable la qualité du test logiciel. En effet, l’amélioration de la qualité des tests commence déjà par la lisibilité du format du test. Il est beaucoup plus simple de vérifier un scénario de test écrit en langage naturel Gherkin et de vérifier le jeu de données associé (voir Figure 2), que de vérifier des tests écrits en code avec un jeu de données réparti sur plusieurs lignes de code. Le format d’écriture d’un scénario de test BDD favorise la vérification de la complétude des jeux de données. Les tests BDD sont encore plus simples à lire et à vérifier et sont plus rapides à écrire et à lire. - -Les scénarios de tests BDD permettent aux équipes de développement d’apporter plus d’expressivité dans l’écriture des scénarios de tests. En effet, quand les développeurs implémentent les tests après l’implémentation des fonctionnalités, ils sont limités par l’expressivité des composants logiciels déjà implémentés. En écrivant les scénarios de tests au même moment que les exigences, ils peuvent écrire des scénarios qui appellent à la fois des composants logiciels et matériels. L’implémentation de ces tests passent par l’implémentation de composants Stub. Ces pratiques favorisent l’implémentation de tests ayant un important taux de couverture fonctionnelle. Le taux de couverture fonctionnelle est un indicateur pertinent de qualité et pourrait être amélioré et piloté par l’utilisation des techniques de BDD. - -**Efficience des équipes de développement** : la mise en œuvre d’une approche BDD permet aux équipes de développement d’exploiter directement des scénarios de tests validés sans passer du temps à les écrire. Nous pensons que les scénarios de tests doivent être écrits par l’équipe fonctionnelle et que l’équipe de développement doit se contenter d’implémenter ces scénarios sans les modifier. Ainsi, l’équipe de développement se contente de générer le code des tests, de les compléter, pour ensuite les exécuter et générer le rapport de tests complet. - -L’utilisation des outils automatiques des Framework BDD (génération de code des tests, exécution du jeu de données entier, génération des rapports de tests (Voir figure 4)) permet à l’équipe de développement de gagner en efficience. Le BDD n’est plus seulement une démarche pour améliorer et vérifier la capture des besoins mais un ensemble d’outils pour optimiser le cycle de vie de développement logiciel. Il permet de laisser une trace documentaire à jour aidant à maintenir l’intégrité des développements. - -## IV. Remplacer une exigence par des scénarios BDD - -Pour certains projets de certains acteurs industriels chez qui nous sommes intervenus, nous avons préconisé de remplacer la définition d’exigences par la description de scénarios de test. Un scénario de test complet avec un jeu de données complet peut remplacer une exigence. Cela peut être fait sous certaines conditions : - -- L’équipe de développement doit maîtriser les techniques BDD et être mature sur la technologie. -- L’équipe de développement doit mettre en place des standards d’architecture logicielle et d’écriture de scénario de tests pour maintenir l’intégrité et la cohérence des exigences. -- Associer un scénario de test, qui remplace l’exigence, à un composant logiciel. -- Les scénarios de tests doivent être écrits sous forme de tests logiciels de type boites noires et boites blanches. - -Pour réduire l’effort de test et standardiser l’écriture des exigences, certaines équipes ont choisi d’utiliser du pseudo code ou de la preuve formelle dans l’écriture des exigences. Cela nécessite des compétences chez l’équipe fonctionnelle pour maîtriser l’algorithmique et les langages formels, ce qui représente un frein réel et une source de génération d’anomalies. Cependant, ces problèmes peuvent être surmontés quand l’exigence est décrite sous forme de scénarios de tests avec un langage naturel simple et facile à lire et à écrire. - -## V. Le BDD pour le développement de logiciels critiques - -Le développement de logiciels critiques, pour certains domaines (aéronautique, médical, automobile, ferroviaire, nucléaire, etc.) s’appuie sur des normes, qui déclinent tous de l’IEC 61508 [iec61508]. Chaque norme décrit des objectifs à atteindre (les exigences sont en cohérence avec l’architecture, le code respecte les standards de codage définis, toutes les exigences sont testées, etc.) pour garantir un certain niveau de fiabilité, qui consiste à démontrer et à garantir que le logiciel livré implémente exactement les fonctionnalités souhaitées. La liste des objectifs à atteindre diffère d’un niveau de criticité à un autre. - -Dans cette section, et en partant de nos expériences de développement, nous présentons comment le BDD peut répondre à certains objectifs des normes DO-178C/ED12C (développement des logiciels aéronautiques) et IEC 62304 (développements de logiciels pour les dispositifs médicaux (DM)). - -Dans la norme DO-178C/ED12C, nous pouvons utiliser les techniques de BDD pour répondre à des objectifs de vérification des exigences de haut niveau de type HLR (High Level Requirement) et des exigences de bas niveau de type LLR (Low Level Requirement) (voir Figure 5.). Les scénarios de tests sont écrits pour produire les tests associés aux exigences (HLT : High Level Test et LLT : Low Level Test). - -{{< figure src="/img/article_bdd/2020_09_09_bdd_figure_5.png" title="Figure 5. Utilisation du BDD pour satisfaire des objectifs de vérification des HLT dans la DO-178C" width="850">}} - -**High/Low Level requirements are accurate and consistent** : grâce à une approche BDD où les scénarios de tests sont écrits en même temps que les exigences, nous pouvons nous assurer du niveau de précision de l’exigence en analysant les scénarios de tests associés ainsi que les jeux de données correspondants. Il s’agit de vérifier l’absence d’ambiguïté dans l’écriture des tests et de réaliser une analyse comportementale de l’exigence. L’activité de vérification consiste également à vérifier la cohérence entre l’exigence et le scénario de test en analysant la cohérence des entrées/sorties, la complétude des scénarios de tests, la couverture des classes d’équivalences et des valeurs limites. Le langage de description des scénarios de tests en BDD favorise ces vérifications grâce au format simple en lecture. - -**High/Low level requirements are verifiable**: l’association de scenarios de test BDD aux exigences tôt dans le cycle de vie de développement logiciel nous permet de disposer d’exigences couvertes par des tests et donc vérifiables. Pour valider qu’une exigence est vérifiable, il suffit de vérifier s’il y a des scénarios de tests BDD qui lui sont rattachés. Les scénarios de tests décrivent les cas de tests de l’exigence. Ils seront ensuite implémentés et exécutés pour valider le code des exigences. Une traçabilité de type Scénario de test / Exigence complète avec une vérification de la complétude des cas de tests peut répondre à cet objectif. - -L’utilisation d’une approche BDD facilite également la mise en œuvre des activités de vérification tout en assurant l’indépendance des rôles. L ’indépendance des rôles exige une séparation des acteurs qui développent les données et ceux qui la vérifient. Grâce au format lisible et compact des scénarios de tests BDD (voir Figure 2), l’équipe fonctionnelle peut facilement développer les cas de test, sans disposer obligatoirement de connaissances techniques. D’autres personnes de l’équipe fonctionnelle peuvent facilement analyser ces données dans le cadre des activités de vérification. Le BDD permet de fluidifier les activités de relecture et de validation de la cohérence entre les HLR et les HLT et entre les LLR et LLT. - -Dans la norme IEC 62304, nous pouvons utiliser les techniques de BDD de la même manière qu’en DO-178C pour répondre à des objectifs de développement et de vérification des exigences logicielles. La norme exige le développement d’exigences logicielles cohérentes, précises et vérifiables (voir figure 6). - -{{< figure src="/img/article_bdd/2020_09_09_bdd_figure_6.png" title="Figure 6. Développement d’exigences vérifiables dans la norme IEC 62304" width="700">}} - -## VI. Le Framework XReq - -{{< figure src="/img/article_bdd/2020_09_09_bdd_xreq.png" width="350">}} - -**XReq (eXecutable Requirements)** est un Framework BDD, développé et commercialisé par Sogilis. XReq est dédié au développement de logiciels embarqués et peut être utilisé dans le développement de logiciels critiques en conformité avec des normes industrielles sectorielles (DO-178B/C, IEC-62304, EN-50128, ISO 26262, etc.). - - **XReq** propose une syntaxe d’écriture des scénarios de tests inspirée du langage Gherkin avec des extensions dans le mode d’écriture du contexte des scénarios et dans la description des jeux de données. XReq permet la génération de code des tests en C/C++ et en Ada (squelettes des étapes de tests) (voir figure 7). La génération de code et le moteur d’exécution des tests dans XReq sont dédiés aux applications et aux environnements embarqués. Il est possible d’utiliser XReq pour exécuter des tests en mode natif ou en mode croisé sur la plateforme cible. - -{{< figure src="/img/article_bdd/2020_09_09_bdd_figure_7.png" title="Figure 7. Description des cas de tests et génération du code des step_definitions avec XReq">}} - -Après exécution des tests, **XReq** génère un rapport détaillant le statut d’exécution des scénarios de tests en précisant l’état de chaque étape des scénarios (step_def) (voir figure 8). - -Aujourd’hui, le **Framework XReq** est utilisé dans plusieurs projets industriels, à la fois dans le développement de logiciels industriels et dans le développement de logiciels critiques normés. - -{{< figure src="/img/article_bdd/2020_09_09_bdd_figure_8.png" title="Figure 8. Rapport de test généré par XReq">}} - -### A. Développement de logiciels normés - -Le BDD, à travers XReq est utilisé dans le cadre du développement d’un contrôleur de vol certifié en conformité avec la DO-178C DAL A [Pulsar]. Une des principales raisons du développement de XReq par Sogilis est l’absence sur le marché d’un Framework BDD dédié aux applications embarquées et pour les logiciels critiques. L’utilisation du BDD et de XReq a été introduite dans les plans de certification décrivant les activités des processus de développement et de vérification des données logicielles. Dans ce projet, les équipes de développement ont produit jusqu’à aujourd’hui avec XReq plus de 600 cas de test de haut niveau en face des HLR (~7000 assertions implémentées dans les blocs « Then ») et plus de 1000 cas de test de bas niveau en face des LLR ((~8000 assertions implémentées dans les blocs « Then »). Grâce à cette approche, nous avons pu stabiliser le temps de gestion des anomalies à ~8% du temps total de développement logiciel. - -XReq, et l’approche BDD d’une manière générale, a apporté de l’efficience dans ce développement logiciel. Cela nous a fortement aidé à la réalisation de plusieurs activités et pour satisfaire plusieurs objectifs de la norme, à savoir : -- Favoriser l’indépendance des rôles dans les activités de vérification : Efficience dans la vérification des scénarios de tests, en simplifiant la vérification des jeux de données par exemple (MC/DC, classes d’équivalence, complétude, valeurs limites, etc.) ; -- Favoriser la séparation des rôles dans l’écriture des cas de test : La personne qui écrit les exigences écrit les scénarios de tests et n’implémente pas la fonctionnalité ; -- Favoriser l’écriture d’exigences vérifiables, précises et cohérentes ; -- Intégration facile de l’environnement de test dans une plateforme d’intégration continue (CI) -- Intégration facile des scénarios de tests dans un environnement de test de type HIL (Hardware -In the Loop) - -### B. Développement de logiciels pour automates PLC - -**XReq** est également utilisé pour améliorer l’efficience et la qualité dans le développement de logiciels industriels de contrôle-commande sûrs et embarqués sur des automates PLC. XReq est utilisé pour vérifier et améliorer la qualité des exigences logicielles. Les tests XReq sont implémentés en face des composants logiciels écrits avec le langage Reflex [Reflex]. Reflex est un langage de programmation de haut niveau, de type GALS « Globalement Asynchrone, Localement Synchrone » basé sur le langage Ada. Il permet d’écrire des applications logicielles de haut niveau pour du prototypage virtuel des applications de contrôle-commande. Une fois l’application Reflex testée et vérifiée en utilisant XReq, nous procédons dans un second temps à la génération de code du logiciel final qui sera déployé sur les équipements d’automates cibles. - -Aujourd’hui, les développeurs des applications d’automates sont confrontés au manque d’outillage pour tester et vérifier les applications implémentées directement avec les outils des constructeurs des PLC. L’utilisation du BDD avec un langage de haut niveau, comme Reflex permet de vérifier les fonctionnalités et de détecter les anomalies tôt dans le cycle de vie de développement applicatif. - -Dans le cadre du développement d’une application simple de gestion d’un réseau de pompes, l’équipe logicielle a produit avec XReq ~60 cas de test en face des exigences avec ~700 vérifications de type « assertion ». A ce stade, les différentes fonctionnalités de l’application ont été testées sans connaissance de plateforme matérielle cible. L’utilisation du BDD a apporté de l’efficience et de la qualité pour les logiciels industriels de contrôle-commande, et cela à plusieurs niveaux : -- Favoriser et améliorer la qualité et la documentation des exigences logicielles (en renforçant l’expression du besoin) et des liens de traçabilité entre les toutes les données produites (voir Figure 9); -- Améliorer la qualité des tests en détectant plus rapidement les cas de tests manquants ; -- Favoriser l’écriture d’exigences vérifiables et de tests logiciels de types « Boite Blanche » et « Boite Noire » -- Intégration facile de l’environnement de test BDD, et de ses rapports d’exécution, dans une plateforme d’intégration continue (CI). - -{{< figure src="/img/article_bdd/2020_09_09_bdd_figure_9.png" title="Figure 9. De l’exigence logicielle au rapport de test : Utilisation de XReq et Reflex dans le développement d’applications de contrôle-commande">}} - -## VII. Conclusion - -En résumé, le BDD est une approche qui réinvente les stratégies de test et les processus de développement logiciel. Elle permet de réduire considérablement les anomalies dont l’origine est l’imprécision ou l’incohérence des exigences logicielles. En effet, la scénarisation des exigences renforce la qualité de l’expression des besoins et de leur utilisabilité. Nous avons exposé dans cet article comment le BDD peut être compatible et utilisable pour le développement de logiciels industriels critiques et normés, et comment on peut en tirer profit au niveau de la qualité et de l’efficience. - -## VIII. Références - -**[ChaosReport]** : Standish Group: “Standish Group Chaos Report” - -**[EcoImp]** : The Economic Impacts of Inadequate Infrastructure for Software Testing, Studies by the National Institute of Standards and Technology and the National Aeronautics and Space Administration, - -https://www.nist.gov/system/files/documents/director/planning/report02-3.pdf - -**[Xreq]** : https://sogilis.com/services-safety-critical.html - -**[Gherkin]** : https://cucumber.io/docs/gherkin/reference/ - -**[Cucumber]** : https://cucumber.io/ - -**[Specflow]** : https://specflow.org/ - -**[Jbehave]** : https://jbehave.org/ - -**[Concordion]** : https://concordion.org/ - -**[iec61805]** : International Electrotechnical Commission, “Functional Safety and IEC 61805”, - -https://www.iec.ch/functionalsafety/standards/ - -**[Pulsar]** : Pulsar Flight System: https://www.hionos.com/#pulsar - -**[Reflex]** : https://www.artics.fr/produits - diff --git a/site/content/posts/2020-09-15-niveau-qualite-requis.md b/site/content/posts/2020-09-15-niveau-qualite-requis.md deleted file mode 100644 index f4d701e38..000000000 --- a/site/content/posts/2020-09-15-niveau-qualite-requis.md +++ /dev/null @@ -1,199 +0,0 @@ ---- -title: Quel besoin en Qualité Logicielle ? -author: Amin -date: 2020-09-15 -image: /img/article_niveau_qualite/2020_09_09_quality.jpg -categories: - - DÉVELOPPEMENT -tags: - - code - - dev - ---- -***Résumé—* De plus en plus de composants logiciels sont intégrés dans les solutions et les systèmes développés par l’industrie, avec des contraintes de fiabilité de plus en plus fortes. La défaillance de ces systèmes n’entraîne pas obligatoirement de blessures ou de pertes de vies humaines. Pourtant ces systèmes restent critiques, car leur défaillance pourrait avoir un impact économique important. La fiabilité de ces solutions est fortement couplée à la fiabilité des logiciels qu’elles embarquent, avec un besoin grandissant de prouver cette fiabilité, auprès des clients et des marchés cibles. Pour la plupart des solutions développées, n’impliquant pas de risques humains, il n’existe pas, à ce jour, de standard pouvant être appliqué et à mis en œuvre pour qualifier la fiabilité logicielle.** - -**Cet article s’intéresse à la mise en œuvre d’une approche permettant de mesurer le niveau de qualité requis du logiciel à développer, pour garantir un certain niveau de fiabilité de la solution. Cet article souligne les difficultés rencontrées par les acteurs industriels, et discute quelques solutions possibles. Il explique également pourquoi la qualité logicielle ne doit pas être considérée comme une variable d’ajustement dans un projet de développement, et comment cela pourrait se traduire avec la mise en œuvre de méthodes et outils adéquats.** - -## I. Introduction - -Dans le développement des logiciels non normés (sans objectif de certification en conformité d’une norme donnée), la mise en œuvre d’activités techniques pour garantir un certain niveau de qualité est souvent considérée comme un surcoût et une contrainte supplémentaire aux équipes, entraînant le ralentissement de la production et des livraisons. Les activités techniques peuvent être variées, différentes et mises en œuvre de plusieurs manières (écriture et validation des exigences logicielles, déroulement d’une stratégie de tests, assurance d’une couverture de code fonctionnelle, assurance de la complétude des exigences, revue de code et des résultats de tests, etc.). - -D’une manière générale, dans le montage et lors du suivi d’avancement des projets de développement logiciel, trois variables sont définies et ajustées : le périmètre fonctionnel, le budget et les délais de livraison. Les discussions autour de la qualité logicielle apparaissent quand les problèmes de fiabilité, de maintenabilité et de livraisons deviennent critiques et visibles. L’ajout et la maintenance de fonctionnalités peuvent devenir rapidement des tâches très coûteuses et les délais de livraisons deviennent difficiles à respecter. En conséquence, les budgets pourraient être rapidement dépassés. - -Certaines tentatives d’ajustement des variables initiales sont appliquées pour reprendre en main et maîtriser les projets qui dérivent, en particulier, sur les aspects de budget et de qualité. Les stratégies peuvent être différentes, allant de la réduction du périmètre fonctionnel, à l’augmentation du budget ou même au décalage des dates de livraison. Certaines entreprises décident à ce stade de restructurer les équipes en montant une équipe d’assurance qualité (QA). Le rôle de cette équipe consiste à identifier et rattraper les défaillances au niveau de la qualité. Cette solution peut s’avérer efficace si les équipes d’assurance qualité logicielle réussissent à définir le bon niveau de qualité requis et travaillent conjointement avec les équipes de développement pour mettre en place les bonnes activités techniques, ce qui n’est pas toujours simple, selon les organismes. - -En pratique, les activités techniques de qualité, dans le cycle de développement de logiciel ne sont pas menées par les équipes de développement dès le début des projets, car on considère généralement que les premières phases de prototypage ne requièrent pas un niveau qualité à garantir. Le but étant de sortir un prototype fonctionnel, dans des délais très courts. Les activités techniques de qualité sont ainsi repoussées à la phase de développement industrialisé. - -Généralement, durant la phase de développement industrialisé, les équipes logicielles se rendent compte de l’existence d’une dette technique considérable (Exemple : beaucoup de tests à écrire pour couvrir le code précédemment produit), ce qui implique la nécessité de modifier potentiellement l’architecture logicielle et faire certains choix (changer de technologies par exemple). Plusieurs équipes de développement ont souvent suggéré de reprendre les développements « from scratch » au lieu d’essayer de réutiliser le code existant, en améliorant sa qualité. Les activités de qualités ont certainement un coût supplémentaire, mais ne pas les introduire ou les introduire tard risque de coûter encore plus cher, en coût de maintenance et d’évolution. - -Nous pensons qu’il est toujours pertinent de définir et à mettre en œuvre un niveau de qualité à garantir dès le démarrage du projet. Ce qui consiste à considérer les aspects de qualité au même niveau que le périmètre fonctionnel, le budget et les délais. Cette configuration à 4 axes ramène plus de cohérence au périmètre fonctionnel et aux aspects budgétaires. La prise en compte de cette variable dès le début permet d’ajuster dès le démarrage les autres variables. L’intégration des activités de qualité tôt dans le projet peut ramener une garantie continue de la fiabilité des développements et apporte de l’efficience au sein de l’équipe développement, tout en respectant les budgets et les délais. - -{{< figure src="/img/article_niveau_qualite/2020_09_15_qualite_figure_1.png" title="Figure 1. Définition des besoins initiaux d’un projet de développement logiciel">}} - -Durant ces dernières années, nous sommes intervenus chez plusieurs acteurs industriels afin d’évaluer des logiciels existants et pour préconiser des activités de qualité adéquates. Nous nous sommes retrouvés à maintes reprises face à des questions de type « Combien va coûter la mise en place de nouvelles activités techniques ? Combien l’entreprise va gagner une fois ces activités en place ? Ce n’est pas de la sur-qualité ce que vous proposez ? etc. ». Nous remarquons toujours une inquiétude et une crainte d’avoir engagé des activités sans intérêts, souvent qualifiées de sur-qualité, sans pouvoir dimensionner les surcoûts liés au manque de qualité. Malheureusement, très peu d’organismes savent ce que leur coûte la non-qualité, une approche basée sur des organisations et des équipes historiquement en place étant très souvent la règle. Ce type de retour est compréhensible puisqu’il n’existe généralement pas de niveau de qualité défini, et spécifié comme étant un objectif. - -Les activités techniques de qualité sont encore plus nombreuses et plus compliquées à modéliser et à mettre en œuvre dans le cadre d’un développement logiciel faisant partie d’un système Logiciel/Matériel. Les activités de tests ne doivent pas se limiter aux composants logiciels mais doivent prendre en compte le comportement du matériel, ce qui représente une complexité et un coût supplémentaire à prévoir. - -Cet article aborde le problème de définition d’un niveau qualité adéquat pour les développements logiciels, non soumis à une certification en conformité avec des standards, en soulignant la pertinence de prendre en considération les aspects de qualité au même niveau que le budget, les délais et le périmètre fonctionnel. Il explique les moyens possibles pour s’inspirer des standards existants afin de proposer une approche permettant d’identifier les activités techniques de qualité nécessaires à mettre en œuvre. Cet article explique l’intérêt d’investir dans la qualité logicielle pour éviter des surcoûts de maintenance futures. - -La section II présente un état de l’art des standards existants ainsi que les niveaux de criticité ciblés. La section III discute la manière de définir et de garantir des objectifs de qualité, pour les logiciels non soumis à des normes, en se basant sur les standards existants, et en prenant en compte les exigences du marché. La section IV démontre l’importance d’investir dans la qualité, tôt dans le processus de développement logiciel. Elle explique également l’importance de ne pas considérer la qualité comme une variable d’ajustement pour garantir une fiabilité continue des logiciels développés. - -## II. Développements normés de logiciels critiques - -Le développement de logiciels critiques, pour certains domaines (aéronautique, médical, automobile, ferroviaire, nucléaire, etc.) s’appuie sur des normes, qui déclinent tous de l’IEC 61508 [iec61508]. - -{{< figure src="/img/article_niveau_qualite/2020_09_15_qualite_figure_2.png" title="Figure 2. Standards pour le développement de logiciels critiques sectoriels" width="700" align="center">}} - -Chaque norme décrit des objectifs à atteindre (les exigences sont en cohérence avec l’architecture, le code respecte les standards de codage définis, toutes les exigences sont testées, etc.) pour garantir un certain niveau de fiabilité, qui consiste à démontrer et à garantir que le logiciel livré implémente exactement les fonctionnalités souhaitées. La liste des objectifs à atteindre diffère d’un niveau de criticité à un autre. Le tableau ci-dessous illustre les principales normes ainsi que les niveaux de criticité associés. La norme DO-178C/ED12C s’intéresse au développement des logiciels aéronautiques. La norme IEC 62304 s’applique aux développements de logiciels pour les dispositifs médicaux (DM). Les normes EN 50128 et ISO 26262 s’appliquent respectivement aux développements de logiciels ferroviaire et automobile. - -Chaque niveau de criticité (A, B, C, DAL-A, SIL1, SIL C, etc.) définit un ensemble d’objectifs à atteindre et d’activités à dérouler. La liste des activités techniques et la rigueur de leurs déroulements peuvent varier d’un niveau de criticité à un autre. Par exemple, dans la norme aéronautique DO-178C, et pour le niveau de criticité « DAL-A » les activités de vérification d’une écriture d’une exigence, nécessitent obligatoirement une indépendance des rôles (Les personnes qui écrivent l’exigence et qui la vérifient dans un second temps, sont obligatoirement des personnes différentes). Cette indépendance des rôles n’est pas exigée dans un contexte de développement en conformité avec le niveau de criticité « DAL-C ». - -{{< figure src="/img/article_niveau_qualite/2020_09_15_qualite_tableau_1.png" title="Tableau 1. Niveaux de criticité associés aux standards" width="850">}} - -Les niveaux de criticité sont établis selon l’importance des fonctionnalités à développer et l’impact de leurs défaillances, sur les vies humaines. Par exemple, dans la norme médicale IEC 62304, le niveau « C » est le plus haut niveau de criticité. Il correspond à des fonctionnalités pouvant, en cas défaillance, causer la mort ou des blessures humaines sérieuses. Le niveau « B » est associé aux fonctionnalités pouvant causer des blessures non sérieuses et le niveau A pour les fonctionnalités sans impacts sur la santé (voir figure 2). - -Dans le développement normé, et avec l’expérience des années, nous avons une idée des coûts approximatifs des développements logiciels, selon les niveaux de criticité et les niveaux de qualité requis. Par exemple, dans le standard aéronautique DO-178C, il existe cinq niveaux différents, allant du DAL-A au DAL-E, le moins critique (voir tableau). À mesure que le niveau de criticité augmente, le niveau de rigueur associé aux activités techniques augmente également, et par conséquence les coûts augmentent et les délais de livraison se rallongent. - -{{< figure src="/img/article_niveau_qualite/2020_09_15_qualite_figure_3.png" title="Figure 3. Les niveaux de criticité de la norme IEC-62304" width="350">}} - -D’après [Hilderman2009], les coûts associés au niveau D sont 15% supérieurs au développement logiciel du grand marché, qui dispose d’une qualité moyenne. D’après [Louis2019] [Hilderman2009], les coûts supplémentaires entre un développement en DAL-D et un DAL-C est estimé à 30%. La différence en coût entre le niveau C et B est de 15% supplémentaire alors que l’écart entre le niveau A et B est de 5%. D’après notre expérience dans le développement aéronautique, le développement en DAL-A représente un surcoût pouvant varier entre 55% et 70% par rapport à un développement classique, sans activité techniques de qualité. - -Pour les applications industrielles, le standard IEC61508 reste largement utilisé. Il s’agit d’un standard à caractère générique qui cible l’obtention de la sécurité fonctionnelle des systèmes, basée sur les risques. Les activités techniques de développement ciblent la détection des pannes aléatoires et systématiques des systèmes, au niveau matériel et logiciel. Cette norme n’a pas été conçue dans un esprit de certification puisqu’elle n’est rattachée à aucune réglementation, et reste un sujet à interprétations divergentes. Elle présente également des inconvénients au niveau de l’attribution du niveau de criticité au logiciel et par rapport à sa capacité à adresser des nouvelles solutions émergentes comme l’IoT, et les applications orientées industrie 4.0. Des travaux sont en cours pour développer une nouvelle norme IEC 63187, pour mieux prendre en compte certains aspects comme l’ingénierie système, ainsi que les exigences et les menaces liées à la cybersécurité. - -Aujourd’hui, la gestion des aspects de qualité dans le développement est largement mieux maîtrisée en industrie en présence d’une norme sectorielle, qui encadre les développements, et quand la certification logicielle fait partie d’un cadre réglementaire. - -D’un autre côté, l’industrie logicielle a connu ce qu’on appelle une « Crise Logicielle ». Ce terme est apparu à la fin des années 1960 avec la montée en complexité des logiciels et l’apparition de plusieurs problèmes comme les dépassements de budgets et de délais. La qualité du logiciel livré était souvent déficiente ne tenant pas en compte le besoin utilisateur. Les coûts de la maintenance du logiciel (coût, régressions, complexité) n’étaient pas maîtrisés. La complexité du logiciel et son architecture était une conséquence directe de l’évolution du matériel et de la disponibilité des ressources [Yu2009] [Dij79]. - -En réponse à cette première crise, l’industrie logicielle a introduit des méthodes et des outils de gestion du cycle de vie du développement, en adoptant des approches de développement en cascade et des outils, en particulier des ateliers de développement. Cependant, les problèmes de l’industrie logicielle persistent encore aujourd’hui. Nous continuons de voir toujours les mêmes symptômes de manque de qualité et de dérive financière et temporelle. Dans [Gibbs94], les auteurs parlent d’une crise chronique. D’autres personnes [Fitzgerald2012], considèrent qu’on est en train de vivre la crise logicielle 2.0, puisque les applications logicielles et les technologies ne cessent d’augmenter en complexité. En parallèle, cette complexité n’est pas suivie par le développement de nouvelles méthodes et outils permettant de garantir à la fois la fiabilité et la qualité, tout en respectant les délais et les budgets. - -L’industrie du logiciel évolue vite et continue sa progression en termes de complexité. Aujourd’hui, dans un seul système, on peut trouver plusieurs briques logicielles différentes et communicantes, qu’il faut développer, vérifier et livrer. Il pourrait s’agir de développement de logiciel embarqué, de protocoles de communications, d’applications mobiles, d’applications dans le Cloud avec une architecture micro-services et une gestion de données volumineuses, de bases de données NoSQL, d’applications Web, etc. - -En conclusion, la qualité ainsi que les dépassements budgétaires et temporels ont toujours été des problèmes dans l’histoire de l’industrie logicielle. Ces problèmes sont nettement mieux gérés quand les développements sont encadrés par des normes et par une réglementation, ce qui est le cas des logiciels développés pour des systèmes critiques. Dans [Spector86], l’auteur estime que les logiciels ne sont jamais développés à temps et dérivent en budget. De plus, ils contiennent plusieurs anomalies. Dans cet article, l’auteur fait une comparaison entre les projets de construction de ponts, qu’il estime efficaces par rapport aux projets de développement de logiciels qui se terminent avec des dérives, sans atteindre les fonctionnalités souhaitées. - -Le Chaos Report [Chaosreport18] mentionne que plus de 55% des projets dérivent de plus de 50% de budget, et peuvent atteindre jusqu’à plus de 400%. Seulement 15% des projets dérivent de moins de 20%. La dérive de délais de livraison est supérieure à 50% pour ~70% des projets. -Dans cet article, nous nous intéressons à la définition et à la mise en œuvre du niveau de qualité requis pour un logiciel donné, sans mettre en place de norme et sans faire de la sur-qualité. Nous aborderons les moyens d’adapter les activités techniques de qualité avec les nouveaux besoins du marché et les nouvelles méthodologies de développement. - -## III. Définir un niveau de qualité logicielle requis - -Le développement logiciel ne consiste pas uniquement à développer du code. Les données logicielles comportent le code source, les exigences de haut niveau, les exigences détaillées, les tests, les données d’architecture, les données de gestion de projet, etc. C’est grâce à la bonne implémentation de toutes ces données, que l’on peut garantir un niveau de fiabilité et de confiance du code source exécutable, livré et déployé en production. C’est grâce à ces données également que l’on peut garantir une maintenance maîtrisée du logiciel. - -### A. Définition et Calcul d’un niveau de qualité - -La définition d’un niveau de qualité requis d’un logiciel peut se faire lors du démarrage d’un projet ou pendant la phase d’un développement en cours. Il est évident qu’il est beaucoup plus simple et efficace de prendre en compte les aspects de la qualité tôt dans le processus de développement logiciel. D’après notre expérience, la plupart des demandes d’amélioration de la qualité logicielles surviennent durant des phases avancées de développement. Les interventions sur ces projets surviennent pour les besoins suivants : - -- L’industriel qui remarque que son projet dérive et lui échappe et souhaite reprendre la maîtrise et garantir un certain niveau de qualité auprès de son marché. Il s’agit d’industriels en train de perdre des parts de marché à cause du manque de fiabilité et de la qualité déficiente de leurs logiciels. -- L’industriel n’a pas beaucoup avancé sur le projet et souhaite analyser et faire évoluer les activités techniques de qualité déjà mise en œuvre. Il s’agit d’industriels qui souhaitent garantir un niveau de qualité adéquat. - -Pour mesurer et qualifier le niveau de qualité requis, nous proposons une approche générale qui se base sur une analyse de la solution à développer à plusieurs niveaux. Cette analyse prend en compte à la fois les aspects fonctionnels, les exigences du marché, les aspects systèmes, la stratégie de développement et l’analyse de l’existant si le logiciel ciblé est en cours de développement. - -{{< figure src="/img/article_niveau_qualite/2020_09_15_qualite_figure_4.png" title="Figure 4. Flot d’analyse du niveau de qualité logicielle requis">}} - -Pour les aspects fonctionnels (figure 4.1), une analyse de risques est requise pour identifier les fonctionnalités les plus importantes, et analyser les conséquences de leurs défaillances au regard du contexte métier. A ce niveau, les risques encourus par le client en relation avec l’usage et au contexte métier sont prises en compte. Des exigences de sécurité ou de cybersécurité peuvent en être déclinées à partir de cette analyse. La volumétrie et la criticité des fonctionnalités du logiciel et l’importance des exigences de sécurité pourraient être des indicateurs importants à prendre en compte dans la définition d’un niveau de qualité requis. - -L’analyse de l’architecture système (figure 4.2) est nécessaire pour évaluer le niveau de tests requis à mettre en place. L’architecture système représente le logiciel qu’on souhaite développer dans son environnement global (solution IoT, pilotage d’une machine, logiciel embarqué sur une carte électronique pouvant être disponible ou en cours de conception, logiciel utilisé pour communiquer et piloter un autre logiciel, logiciel mobile qui doit se synchroniser avec d’autres applications, etc.). La complexité de l’architecture système est importante pour identifier les activités techniques de qualité requises. L’analyse de la démarche de développement (figure 4.3) consiste à analyser les éventuelles phases de prototypage prévues, et leurs objectifs. Nous analysons également la démarche de réutiliser les prototypes, et le nombre ainsi que le taux de complexité des verrous techniques à lever. Nous analysons à ce stade le taux de couverture des verrous techniques par les prototypes. - -L’analyse de l’existant (figure 4.4) concerne uniquement les logiciels en cours de développement. Il s’agit d’identifier les déficiences en qualité et de mesurer la dette technique associée. Nous analysons également les activités techniques de qualité déjà existantes. - -Le niveau de qualité global requis (figure 4) d’un développement logiciel est calculé à partir du besoin en qualité technique croisé avec le besoin en qualité fonctionnelle. Ces métriques sont suffisantes pour un projet en cours de démarrage. Pour les projets en cours, nous rajoutons des métriques liées à la dette technique. - -La qualité technique requise (figure 5) est mesurée à partir du niveau de complexité de l’architecture système et de la démarche de développement adoptée (prototypes, objectifs des prototypes, couverture des verrous techniques, etc.). La qualité fonctionnelle est mesurée à partir de l’analyse fonctionnelles des risques en identifiant les fonctionnalités critiques et leur poids dans l’application, ainsi que les exigences du marché et le contexte métier du logiciel. - -Pour simplifier la mesure des métriques, nous avons défini 3 niveaux de complexité par mesure : -- L (Low) => Complexité Basse -- M (Medium) => Complexité Moyenne -- H (High) => Complexité Elevée - -{{< figure src="/img/article_niveau_qualite/2020_09_15_qualite_figure_5.png" title="Figure 5. Niveau de complexité de la qualité technique requise" width="650">}} - -Dans notre démarche, nous proposons de classifier les niveaux de qualité qui en résultent en trois grandes catégories (voir figure 6). Le niveau 1 concerne le niveau de qualité que l’on voudrait atteindre pour un logiciel faisant partie d’une architecture système simple, sans verrous techniques importants à lever et avec des fonctionnalités non critiques. Le niveau 1 pourrait être un objectif de qualité raisonnable et réaliste pour des logiciels existants, ayant une dette technique élevée. Il peut être considéré comme le canot de sauvetage pour les projets en cours, souffrant de déficience de qualité, et qu’on souhaiterait maîtriser. Dans le niveau 1, nous préconisons le développement de tests fonctionnels en fixant un objectif de couverture de code élevée, sans faire de tests unitaires. L’automatisation des tests fonctionnels est fortement conseillée à ce niveau pour vérifier facilement les régressions. La gestion d’exigences et leurs traçabilités avec les tests fonctionnels est également conseillée. - -Le niveau 2 concerne les logiciels comportant certaines fonctionnalités critiques qui s’intègrent dans une architecture système moyennement compliquée, avec des verrous techniques identifiés et atteignables. Par rapport au niveau 1, des activités techniques de qualité supplémentaires doivent être faites uniquement pour l’implémentation des fonctionnalités critiques. En plus des tests fonctionnels, il est recommandé de faire des tests unitaires, des tests boites noires / blanches sur tous les composants logiciels utilisés par les fonctionnalités critiques. Des bancs de tests sont à développer également pour réaliser les tests sur la plateforme cible, ainsi que des tests systèmes et d’intégration. - -{{< figure src="/img/article_niveau_qualite/2020_09_15_qualite_figure_6.png" title="Figure 6. Calcul du niveau de qualité logicielle requis" width="750">}} - -Le niveau 3 concerne les logiciels comportant plusieurs fonctionnalités critiques dans une architecture système complexe, avec des verrous techniques identifiés et présentant des risques. Par rapport au niveau 2, les activités à faire sont : décrire l’architecture système, vérifier la cohérence entre l’architecture système et les exigences logicielles, gérer les liens de traçabilité, etc. Côté tests, des simulateurs de certains composants du système peuvent être développés pour écrire plus de tests d’intégration, de performance et de gestion d’erreurs (simulation de comportements erronés des sous-systèmes qui communiquent avec le logiciel). Pour le niveau 3, il est recommandé de construire une équipe d’assurance qualité qui va travailler avec les équipes de développement pour garantir la qualité des données produites. Un récapitulatif des niveaux de qualité, et des activités associées, est présenté dans le tableau 2. - -### B. Pratiques d’ingénierie pour répondre aux besoins de la qualité logicielle - -En plus des activités techniques de qualité que nous préconisons pour chaque niveau de qualité requis, nous pensons que la manière de les développer et de les organiser est aussi important que les activités elles-mêmes. Les pratiques d’ingénierie sont complémentaires aux activités techniques pour garantir un bon niveau de qualité et la fiabilité du logiciel. - -Nous préconisons par exemple de mettre l’accent sur l’automatisation des tests, pour vérifier rapidement les fonctionnalités ainsi que les non régressions. La mise en œuvre d’une plateforme d’intégration continue peut faciliter l’automatisation des tests. Mais certains peuvent se poser la question : En quoi l’automatisation des tests peut améliorer la qualité du logiciel ? Pour y répondre, prenons un exemple concret que nous avons vu lors de certaines de nos interventions. Dans une équipe de test logiciel, dont le rôle est de valider les développements logiciels, les testeurs déroulent manuellement un plan de test d’intégration d’un logiciel qui pilote une machine. Plus l’équipe de développement avance et livre de nouvelles fonctionnalités et plus le plan de test à dérouler se rallonge et devient difficile à maintenir. L’équipe de test se retrouve vite débordée. Elle n’arrive plus à finir les tests d’une version avant l’arrivée de la version suivante. L’équipe de test est devenue alors le goulot d’étranglement du cycle de développement logiciel. Elle se trouve obligée de livrer des versions partiellement testées, ce qui représente une source de défaillance de qualité. - -{{< figure src="/img/article_niveau_qualite/2020_09_15_qualite_tableau_2.png" title="Tableau 2. Classification des niveaux de qualité requis pour un développement logiciel non soumis à une norme">}} - -Nous conseillons également une démarche de développement itérative et incrémentale qui représente un cadre pour développer un logiciel de qualité. Cette approche favorise la prise en compte des modifications fonctionnelles, et permet de rendre possible la révision et l’amélioration de certains choix et certains aspects du logiciel. La démarche itérative et incrémentale permet de livrer rapidement des fonctionnalités, avant la fin de la rédaction de toutes les spécifications de l’application. Cela permet d’identifier rapidement les problèmes et les erreurs injectées lors de la phase d’écriture des exigences. - -En adoptant une telle démarche itérative et incrémentale, le cycle de vie de développement logiciel (écriture d’exigences, tests, architecture, code, etc.) est appliqué plusieurs fois. Ces répétitions vont permettre son amélioration continue et l’efficience du processus va s’améliorer considérablement au fur et à mesure que le projet avance. - -Dans les normes décrites dans la section II concernant le développement de logiciels critiques, nous trouvons des objectifs à atteindre décrits et classés selon le niveau de criticité du logiciel. Notre approche de définition d’un niveau de qualité requis, pour les logiciels non soumis à des normes, ne se limite pas à la définition d’objectifs et d’activités techniques. Elle préconise également des pratiques d’ingénierie à mettre en œuvre. Cette vision de la qualité combine à la fois la fiabilité des données logicielles produites et l’efficience des processus et des activités techniques. Rendre certaines activités plus efficientes et plus fiables aura un impact direct sur la maitrise du budget et des délais de livraison. - -Nous pensons qu’il est pertinent que ces pratiques d’ingénierie soient communes à tous les niveaux de qualité requis. Il s’agit d’un ensemble de méthodes et d’outils ayant un coût dans les premières phases du projet. L’objectif de ces pratiques d’ingénierie est de ramener de l’efficience et de la rigueur dans le développement logiciel, en particulier, quand on adopte une démarche itérative et incrémentale. - -En faisant une comparaison des niveaux de qualité introduits dans cette section avec les niveaux de criticité définis dans les normes existantes de développement de logiciels critiques, nous pouvons, à titre indicatif, mentionner les équivalences suivantes : - -- Le niveau 3 peut correspondre au niveau DAL-C (DO-178C) / SIL2 (EN-50128) -- Le niveau 2 peut correspondre à un niveau qui va se positionner entre le DAL-D et le DAL-C (DO-178C) / entre SIL1 et SIL 2 (EN-50128). -- Le niveau 1 peut correspondre au niveau DAL-D (DO-178) / SIL 1 (EN-50128) - -Nous savons qu’un développement en DAL-C peut coûter environ +35% à +40% de surcoût par rapport à un développement classique. Nous estimons que la mise en œuvre du niveau 3 correspond à ~30% de coût supplémentaire à prévoir (activités techniques de qualité et pratiques d’ingénierie), puisque ce niveau n’intègre pas toutes les activités de la DO-178C DAL-C. Pour le niveau 2, nous estimons le coût supplémentaire entre 20% et 25% et entre 10% et 15% pour le niveau 1. - -Ce surcoût reste un très bon investissement comparé aux surcoûts de maintenance pour des développements de logiciels ayant des déficiences de qualité. Dans ce cas, le coût de maintenance représente au moins 60% du coût total de développement logiciel, et ~50 % des coûts de maintenance concernent la gestion et la correction des anomalies. - -### C. Stratégie de Test - -Tout au long de la crise sanitaire liée au virus COVID-19, le mot d’ordre était « faire des tests, plus de tests, et des tests rapides pour identifier le plutôt possibles les personnes contaminées ». Dans le monde du logiciel, les tests sont également indispensables pour maîtriser la qualité du logiciel et garantir sa fiabilité. Les tests permettent de vérifier que les exigences ont été correctement implémentées et pour identifier les anomalies. - -Si l’équipe projet n’écrit pas suffisamment de tests, ou les écrit tard dans le processus de développement, les anomalies seront découvertes également tard dans le cycle de vie de développement logiciel. Il s’agit d’une course pour identifier les erreurs avant qu’elles ne soient plus rattrapables. Le coût de résolution d’une anomalie logicielle évolue exponentiellement en fonction du moment de sa découverte. Dans [Jones Capers], l’auteur estime que le coût de résolution d’une anomalie peut être multiplié par 4 si elle est découverte par les tests unitaires et par 10 si elle est découverte par les tests fonctionnels. Ce coût explose rapidement quand l’anomalie est découverte par des tests systèmes (x 40). Le coût peut être multiplié par 640 quand l’anomalie n’est pas découverte lors du cycle de vie de développement et quand elle apparaît après la livraison. - -Dans les processus de développement logiciel itératifs et incrémentales que nous avons mis en œuvre jusqu’à maintenant, la gestion des anomalies représente 8% à 10% du temps total de développement avec un vélocité de 1,5 jours pour la correction d’une anomalie. La détection rapide des anomalies explique bien ces métriques. - -Ainsi, la qualité logicielle devient une variable indispensable pour l’équilibre des projets (maîtrise de budget, maîtrise des délais). Considérer la qualité comme une variable d’ajustement dans un projet implique forcément une perte de la maîtrise du projet au niveau de la fiabilité, du budget et des délais. - -Pour identifier les anomalies et les maîtriser tôt dans le cycle de vie de développement logiciel, nous proposons dans les niveaux de qualité requis plusieurs activités techniques et des pratiques d’ingénierie associées. L’objectif des activités techniques est de fournir une couverture complète du code et des scénarios d’exécution des fonctionnalités à travers des tests (unitaires, fonctionnels, systèmes, boites blanches, boites noires, etc.), ayant un jeu données complet. L’objectif des pratiques d’ingénierie est de garantir la rapidité de l’exécution des tests et leur intégration au plus tôt dans les processus de développement. Cela peut être fait en utilisant les techniques et les outils TDD (Test Driven Development) et BDD (Behaviour Driven Development). Nous préconisons l’écriture des scénarios de tests au même moment que les exigences, et par la même personne pour éviter de potentielles incompréhensions des exigences par les développeurs. Nous estimons que la personne qui écrit l’exigence est la mieux placée pour décrire le test correspondant. Ainsi, nous pouvons vérifier très tôt dans le processus que toutes les exigences sont vérifiables puisqu’on dispose d’un scénario de test avec un jeu de données par exigence. L’équipe de développement implémente l’exigence et le scénario de test en même temps, sans le modifier. - -{{< figure src="/img/article_niveau_qualite/2020_09_15_qualite_figure_7.png" title="Figure 7. Détection des anomalies : Impact sur les coûts de corrections [Capers2008] [Hicken2018]" width="700">}} - -L’intégration d’un outillage d’automatisation d’exécution des tests TDD/BDD, tel une plateforme d’intégration continue, avec la génération de rapports de tests, donne à l’équipe de développement un outil puissant pour exécuter rapidement les nouveaux tests, ainsi que les tests de non régression. -A Sogilis, nous avons développé le framework de test BDD XReq (eXecutable Requirements) [Xreq], dédié pour le développement de logiciels embarqués (en Ada et en C/C++). Les rapports de tests natifs, ou croisés sont générés et intégrés automatiquement dans la plateforme d’intégration continue. - -### D. Garantir un niveau de qualité en continu - -Parmi les principaux avantages de la mise en œuvre des pratiques d’ingénierie mentionnées dans le tableau 2, nous pouvons évoquer la garantie de livrer à chaque fin d’une tâche une version ayant le niveau de qualité requis des données développées jusque-là. La qualité n’est plus une variable qu’on peut ajuster quand le projet dérape, mais plutôt un critère de livraison de chaque tâche et de chaque incrément du développement logiciel. Combiner les activités techniques de qualité aux pratiques d’ingénierie (Tableau 2) permet d’intégrer plus étroitement les activités de qualité au cycle de vie de développement logiciel. Cela permet d’apporter de façon continue des garanties du niveau de qualité requis. - -La pression de livrer rapidement et en continu des logiciels fonctionnels en respectant des contraintes de coûts et de délais a été toujours un argument des équipes de projets pour laisser de côté les activités de qualité. La solution consiste à coupler fortement les activités de qualité aux activités de développement dans le cycle de vie de développement logiciel. Ce couplage fort passe par une définition claire d’une tâche « terminée / done». Une tâche serait considérée comme « terminée » si et seulement si toutes les activités techniques de développement et de vérification ont été bien réalisées. La plateforme d’intégration continue servira simplement d’outil pour exécuter systématiquement toutes les activités de la chaîne de livraison : compilation, exécution de tous les tests, vérification des cohérences entre les données développées, calcul des métriques de qualité, packaging, etc. - -## IV. Conclusions - -En résumé, nous avons exposé, dans cet article, la problématique de la qualité logicielle des développements non soumis à des normes industrielles sectorielles. Nous avons proposé une approche pour mesurer le niveau de qualité requis d’un logiciel en fonction de plusieurs critères, à savoir, la complexité fonctionnelle, le contexte métier, les exigences du marché, la complexité technique et le niveau de la dette technique. - -Dans notre proposition, l’atteinte du niveau de qualité requis passe par la mise en œuvre d’activités de qualité techniques et par la mise en œuvre de pratiques d’ingénierie innovantes (Tests TDD/BDD, développement itératif et incrémental, Intégration continue, etc.). - -L’investissement dans la qualité logicielle garantit une fiabilité et une qualité continue du logiciel sur sa durée de vie et sur ses versions postérieures. C’est également une garantie pour mieux maîtriser les budgets et les délais, et surtout pour diminuer d’une manière considérable les coûts de maintenance. - -## V. Références bibliographiques - -**[iec61805]** : International Electrotechnical Commission, “Functional Safety and IEC 61805”, https://www.iec.ch/functionalsafety/standards/ - -**[Gibbs94]** : W. Gibbs, “Software's Chronic Crisis”, Scientific American 271 (3), 1994, pp. 86-95. -[Dij79] Edsger Dijkstra, Le programmeur Humble (PDF), Dans Classics en génie logiciel, Yourdon Press, 1979. - -**[Louis2019]** : V. Louis, C. Baron “ Vers une certification continue de logiciels critiques en aéronautique », Novembre 2019, « https://hal.archives-ouvertes.fr/hal-02372069 » - -**[Yu2009]** : A. G. Yu, "Software Crisis, What Software Crisis ?, « 2009 International Conference on Management and Service Science, Wuhan, 2009, pp. 1-4. - -**[Hilderman 2009]** : Hilderman, Vance. “DO-178B Costs Versus Benefits”, https://vancehilderman.com/2015/12/23/do-178c-costs-versus-benefits/ - -**[Fitz2012]** : B. Fitzgerald, "Software Crisis 2.0," in Computer, vol. 45, no. 4, pp. 89-91, April 2012. - -**[Spector86]** : Alfred Z. Spector, David K. Gifford: “A Computer Science Perspective of Bridge Design. Commun. ACM 29(4): 267-283 (1986) - -**[Chaosreport18]** : https://www.standishgroup.com/store/ - -**[Capers2008]** : C. Jones. 2008. Applied Software Measurement: Global Analysis of Productivity and Quality (3 ed.). McGraw-Hill Osborne Media. - -**[Hicken2018]** : A. Hicken. 2018, “The Shift-Left Approach to Software Testing », https://www.stickyminds.com/article/shift-left-approach-software-testing - -**[Xreq]** : https://sogilis.com/services-safety-critical.html - - - diff --git a/site/content/posts/2020-10-27-uas_cybersecurity.md b/site/content/posts/2020-10-27-uas_cybersecurity.md deleted file mode 100644 index dde36d142..000000000 --- a/site/content/posts/2020-10-27-uas_cybersecurity.md +++ /dev/null @@ -1,127 +0,0 @@ ---- -title: Toward Cybersecurity - UAS operations use case -author: Duc, Amin -date: 2020-10-26 -image: /img/article_uas_cybersecurity/2020_10_27_cybersecurity.jpg -categories: - - DÉVELOPPEMENT -tags: - - code - - dev - - cybersecurity - - drone ---- -***Abstract —* For several years, the civil Unmanned Aircraft System (UAS) has become more and more popular with many applications such as aerial photography, goods transportation, surveillance, etc. However, the lack of human observation, communication capacities, and protection makes UAS a good target for cyber-attacks. Therefore, the risk related to the cybersecurity of UAS should be assessed and taken into account in the early phase of UAS development.** - -## I. Introduction - -To deal with the progressive increase in the number of UAS operations, the European Aviation Safety Agency (EASA) classified UAS operations into three categories according to their risk levels: **Open**, **Specific** and **Certified**. In the **Specific** category, we could expect the operation of large drones flying above populated areas, out of the visual line of sight of the pilot and sharing the airspace with manned aircrafts. These operations could pose significant harm to the people overflown and the manned aircraft, especially in the case of a cyber-attack. -To the best of our knowledge, the most common risk assessment methodology in the UAS domain is Specific Operation Risk Assessment (SORA), which is endorsed by the European Union Aviation Safety Agency (EASA) as an acceptable means to fulfill the requirements of the EU regulations related to UAS. However, at this moment, the methodology focuses on only the safety aspect and ignores the cybersecurity aspect. - -## II. General concept of the SORA methodology - -### A. Risk Model - -At this moment, the SORA methodology considers only risks of harms to a person’s life: “fatal injuries to third parties on ground”, “fatal injuries to third parties in air”. To illustrate the risk scenarios related to these harms (or how these harms could happen), the methodology provides a risk model as shown in Figure 1. It includes three major segments: Harms, Hazard, Threats. The direct causes of these harms is a generic hazard “UAS operation out of control” shown in the center of the model. This hazard is defined as an operation being conducted outside of the operator’s intention -(e.g the aircraft flies outside of visual observation of the pilot in a Visual Line Of Sight operation). The hazard could be caused by several threats which are grouped into categories in the left part of the model. Because SORA methodology considers only the safety aspect but not the security aspect, only unintentional threat categories are represented in the model. - -{{< figure src="/img/article_uas_cybersecurity/uas_cybersecurity_001.png" title="Figure 1. Risk Model of the SORA methodology" width="700">}} - -### B. Assessment process - -1) Quantitative approach: -Traditionally, the risk assessment process requires to analyze two parameters of risk: -- Likelihood, -- Severity. - -However, the risks in the SORA methodology is tied to only likelihood parameters because the methodology basically focuses on only risks of harms to the person’s life. The severity of these harms could be considered as extremely high. In other words, the safety objectives will be determined to maintain the likelihood of each harm under the acceptable value (10-6 fatal injuries per flight hour, equivalent to some manned aircraft operation). - -2) Qualitative approach: -The SORA methodology proposes a qualitative approach based on the main ideas of the quantitative approach. First, we determine two qualitative factors: -- Ground Risk Class (GRC), -- Air Risk Class (ARC). - -These factors represent qualitatively the likelihoods that the harms occur in the case of UAS operation out of control. The GRC and ARC are determined based on the intrinsic characteristics of the operation such as operational area, attitude, weight of the UAS and the availability of harm barriers. - -Then, we determine two Specific Assurance and Integrity Levels (SAIL) values, which represent the -level of confidence that the UAS operation will stay under control. One SAIL value corresponds to GRC and the other corresponds to ARC. Then, the higher SAIL value will be chosen as an objective to drive the required safety objectives. - -{{< figure src="/img/article_uas_cybersecurity/uas_cybersecurity_002.png" title="Figure 2. SAIL Determination" width="300" align="center">}} - -## III. Toward Cybersecurity - -Our proposed solution consists of two parts which are called Harm Extension and Threat Extension. - -**Harm Extension** extends the risk scenarios under consideration with new harms; and completes the evaluation of critical level of a given UAS operation. - -**Threat Extension** extends the scenarios under consideration with new cybersecurity threats; and determines the corresponding threat barriers for a given UAS operation. - -In Harm Extension, we concern the harm-side of the risk model. Besides the harms to the person’s life, the public concerns also the other harms such as: -- Privacy Violation -- Physical damages to infrastructure -- Digital damages to infrastructure - -Therefore, these new harms come to mind as important issues that should be taken into account in the methodology. In Harm Extension, our strategy to address the new harms includes four steps as follows: - -1) Chose a new harm that needs to be addressed -2) Determine factors/characteristics of the UAS operation, which have an impact on the likelihood of the chosen harm. -3) Establish formulas or tables to evaluate qualitatively the likelihood based on the determined factors -4) Extend “SAIL determination” step to cover the likelihood of the new harm. - -In Threat Extension, we will concern the threat-side of the risk model. The potential cybersecurity threats need to be identified and grouped in new threat categories. To illustrate the new scenarios, the new threat categories will be added into the threat-side of the risk model as shown in Figure 3. Corresponding to each new threat category, a list of possible threat barriers will be also established. For a given UAS operation, the new threat barriers are chosen in correspondence with the value of the SAIL factor. - -{{< figure src="/img/article_uas_cybersecurity/uas_cybersecurity_003.png" title="Figure 3. Extended risk model" width="700" align="center">}} - -## IV. Cybersecurity for privacy issues in UAS operations - -Nowadays, the privacy violation is one of the most concerned issues for public acceptance of UAS application. However, the general privacy is a very large term and it’s difficult to define it precisely and to address it. We will focus on only three aspects of this harm: - -1) Disclosure of personal information, -2) Illegal personal surveillance, -3) Intrusion into private location. - -We first analyze the likelihood of the privacy violation to determine the possible factors related to this harm, which could be used for the assessment. Then we propose extensions for the assessment process by adding a new step called “Privacy Risk Class (PRC) determination”. With the privacy harm taken into account, the objective of risk assessment is extended to maintain the harm likelihood to privacy under a certain acceptable level. - -Similar to the likelihood of harms to the person’s life, the one of the privacy harm could be decomposed as shown in Figure 4. The combination of the two components (2) and (3) of this equation represents the likelihood that the privacy of third parties is violated after ”UAS operation out of control”. - -{{< figure src="/img/article_uas_cybersecurity/uas_cybersecurity_004.png" title="Figure 4. Likelihood of privacy violation" width="700" align="center">}} - -We identified three intrinsic features of a UAS operation to evaluate the likelihood of privacy violation in case of “UAS operation out of control”: - -- Density of operational area: urban zone vs. rural zone, -- Type of operation: BLOS vs. BVLOS (Beyond Visual Line of Sight), -- Level of detail of the captured images. - -Similar to harms introduced in the classical SORA methodology, the likelihood of privacy harm could be reduced by applying some harm barriers. In this extension, we address three types of harm barriers to mitigate the privacy harms: - -- Privacy protection filters: these algorithms reduce unnecessary information that could violate the privacy of person from the video/image such as Blurring, Pixelization, Masking, Warping -- Restriction on private space: the operator avoids making a flight path across a private space -- Operation-aware announcement to public: the public under observation of a UAS operation should be informed about it. - -The likelihood of privacy violation in the case of “UAS operation out of control” is represented qualitatively by the Privacy Risk Class (PRC) value. The intrinsic PRC value is determined based on the intrinsic features of operation as shown in Figure 5. - -{{< figure src="/img/article_uas_cybersecurity/uas_cybersecurity_005.png" title="Figure 5. Intrinsic PRC Determination" width="600" align="center">}} - -{{< figure src="/img/article_uas_cybersecurity/uas_cybersecurity_006.png" title="Figure 6. PRC Correction factor of harm barriers" width="600" align="center">}} - -Then the determined intrinsic PRC could be reduced by the harm barriers: “Privacy protection filters”, “Restriction on private space” and “Operation-aware announcement to public”. Each harm barrier corrects the intrinsic PRC with a reduction factor shown in figure 6. - -According to figure 5, the intrinsic PRC is at the C level. Upon analysis of the privacy issue, the operator decides to upgrade the onboard camera with a digital filter that makes image of a person blur and unable to be recognized. In this case, the PRC is reduced with 1 level from the C to B level. - -The last step consists in new SAIL determination, the process is described as follows: - -1) Determine a SAIL corresponding to the values of ARC and GRC (see Figure. 2) (classical SORA methodology). This value is called 2D-SAIL. -2) Determine SAIL value corresponding to PRC value. -3) Choose the higher SAIL value (more critical) between the two determined SAIL values as the 3D-SAIL or the final SAIL corresponding to the operation. - -The 2D-SAIL and 3D-SAIL mentioned above are two different values. The main different point between them -is only the way that they are determined. The 2D-SAIL is a combination of GRC and ARC without taking into account PRC (privacy harm). Meanwhile 3D-SAIL takes into account the privacy harm. But both of them represent the level of confidence that “the UAS operation will stay under control” that needs to be achieved. - -## V. Conclusions - -The risk assessment process could be resumed as: - -1) Evaluation of the critical level of a UAS operation based on the likelihood of harms, -2) Determination of safety objectives corresponding to the critical level. - -Based on this concept, we proposed a general approach to extend the existing methodology to support cybersecurity aspect by adding new relevant threats and harms. New harms will be taken into account to evaluate the critical level of the UAS operation. Meanwhile, new threats will be added to anticipate new causes of incidents related to cybersecurity and determine corresponding means of mitigation (or cybersecurity objectives). diff --git a/site/static/.keep b/site/static/.keep deleted file mode 100644 index e69de29bb..000000000 diff --git a/site/static/admin/config.yml b/site/static/admin/config.yml deleted file mode 100644 index 82e314e1b..000000000 --- a/site/static/admin/config.yml +++ /dev/null @@ -1,51 +0,0 @@ -backend: - name: git-gateway -publish_mode: editorial_workflow -media_folder: 'site/static/img' # Folder where user uploaded files should go -public_folder: '/img' # The src attribute for uploaded media will begin with this - -collections: # A list of collections the CMS should be able to edit - - name: 'post' # Used in routes, ie.: /admin/collections/:slug/edit - label: 'Post' # Used in the UI, ie.: "New Post" - folder: 'site/content/posts' # The path to the folder where the documents are stored - create: true # Allow users to create new documents in this collection - fields: # The fields each document in this collection have - - { label: 'Title', name: 'title', widget: 'string' } - - { label: 'Publish Date', name: 'date', widget: 'datetime' } - - { label: 'Intro Blurb', name: 'description', widget: 'text' } - - { - label: 'Image Alt', - name: 'altimage', - widget: 'string', - required: false, - } - - { label: 'Image', name: 'image', widget: 'image', required: false } - - label: 'Tags' - name: 'tags' - widget: 'select' - required: true - multiple: true - options: - [ - 'dev', - 'test', - 'sécurité', - 'design', - 'conférences', - 'process', - 'vie à Sogilis', - ] - - { label: 'Body', name: 'body', widget: 'markdown' } - -# Working with a Local Git Repository -# ============================================================================== -# Beta Features -# added in netlify-cms@2.10.17 / netlify-cms-app@2.11.14 (02/2020) -# Use these features at your own risk. -# https://www.netlifycms.org/docs/beta-features/#working-with-a-local-git-repository - -# when using the default proxy server port -local_backend: true -# when using a custom proxy server port -# local_backend: -# url: http://localhost:8081/api/v1 diff --git a/site/static/admin/index.html b/site/static/admin/index.html deleted file mode 100644 index 13d934321..000000000 --- a/site/static/admin/index.html +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - Content Manager - - - - - - - - diff --git a/site/static/browserconfig.xml b/site/static/browserconfig.xml deleted file mode 100644 index 8991e2536..000000000 --- a/site/static/browserconfig.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - #ff4400 - - - diff --git a/site/static/img/article_bdd/2020_09_09_bdd.jpg b/site/static/img/article_bdd/2020_09_09_bdd.jpg deleted file mode 100644 index 84fa169f5..000000000 Binary files a/site/static/img/article_bdd/2020_09_09_bdd.jpg and /dev/null differ diff --git a/site/static/img/article_bdd/2020_09_09_bdd_figure_1.png b/site/static/img/article_bdd/2020_09_09_bdd_figure_1.png deleted file mode 100644 index 98880cc1f..000000000 Binary files a/site/static/img/article_bdd/2020_09_09_bdd_figure_1.png and /dev/null differ diff --git a/site/static/img/article_bdd/2020_09_09_bdd_figure_2.png b/site/static/img/article_bdd/2020_09_09_bdd_figure_2.png deleted file mode 100644 index 3716891ad..000000000 Binary files a/site/static/img/article_bdd/2020_09_09_bdd_figure_2.png and /dev/null differ diff --git a/site/static/img/article_bdd/2020_09_09_bdd_figure_3.png b/site/static/img/article_bdd/2020_09_09_bdd_figure_3.png deleted file mode 100644 index d952e0019..000000000 Binary files a/site/static/img/article_bdd/2020_09_09_bdd_figure_3.png and /dev/null differ diff --git a/site/static/img/article_bdd/2020_09_09_bdd_figure_4.png b/site/static/img/article_bdd/2020_09_09_bdd_figure_4.png deleted file mode 100644 index d61edc07d..000000000 Binary files a/site/static/img/article_bdd/2020_09_09_bdd_figure_4.png and /dev/null differ diff --git a/site/static/img/article_bdd/2020_09_09_bdd_figure_5.png b/site/static/img/article_bdd/2020_09_09_bdd_figure_5.png deleted file mode 100644 index 9eb62d769..000000000 Binary files a/site/static/img/article_bdd/2020_09_09_bdd_figure_5.png and /dev/null differ diff --git a/site/static/img/article_bdd/2020_09_09_bdd_figure_6.png b/site/static/img/article_bdd/2020_09_09_bdd_figure_6.png deleted file mode 100644 index 508b229a8..000000000 Binary files a/site/static/img/article_bdd/2020_09_09_bdd_figure_6.png and /dev/null differ diff --git a/site/static/img/article_bdd/2020_09_09_bdd_figure_7.png b/site/static/img/article_bdd/2020_09_09_bdd_figure_7.png deleted file mode 100644 index 923bae468..000000000 Binary files a/site/static/img/article_bdd/2020_09_09_bdd_figure_7.png and /dev/null differ diff --git a/site/static/img/article_bdd/2020_09_09_bdd_figure_8.png b/site/static/img/article_bdd/2020_09_09_bdd_figure_8.png deleted file mode 100644 index 35087d8ff..000000000 Binary files a/site/static/img/article_bdd/2020_09_09_bdd_figure_8.png and /dev/null differ diff --git a/site/static/img/article_bdd/2020_09_09_bdd_figure_9.png b/site/static/img/article_bdd/2020_09_09_bdd_figure_9.png deleted file mode 100644 index 849ddd5be..000000000 Binary files a/site/static/img/article_bdd/2020_09_09_bdd_figure_9.png and /dev/null differ diff --git a/site/static/img/article_bdd/2020_09_09_bdd_xreq.png b/site/static/img/article_bdd/2020_09_09_bdd_xreq.png deleted file mode 100644 index 84d3a6dec..000000000 Binary files a/site/static/img/article_bdd/2020_09_09_bdd_xreq.png and /dev/null differ diff --git a/site/static/img/article_niveau_qualite/2020_09_09_quality.jpg b/site/static/img/article_niveau_qualite/2020_09_09_quality.jpg deleted file mode 100644 index 9746db614..000000000 Binary files a/site/static/img/article_niveau_qualite/2020_09_09_quality.jpg and /dev/null differ diff --git a/site/static/img/article_niveau_qualite/2020_09_15_qualite_figure_1.png b/site/static/img/article_niveau_qualite/2020_09_15_qualite_figure_1.png deleted file mode 100644 index 32b311aea..000000000 Binary files a/site/static/img/article_niveau_qualite/2020_09_15_qualite_figure_1.png and /dev/null differ diff --git a/site/static/img/article_niveau_qualite/2020_09_15_qualite_figure_2.png b/site/static/img/article_niveau_qualite/2020_09_15_qualite_figure_2.png deleted file mode 100644 index ea11e0b58..000000000 Binary files a/site/static/img/article_niveau_qualite/2020_09_15_qualite_figure_2.png and /dev/null differ diff --git a/site/static/img/article_niveau_qualite/2020_09_15_qualite_figure_3.png b/site/static/img/article_niveau_qualite/2020_09_15_qualite_figure_3.png deleted file mode 100644 index e744436fc..000000000 Binary files a/site/static/img/article_niveau_qualite/2020_09_15_qualite_figure_3.png and /dev/null differ diff --git a/site/static/img/article_niveau_qualite/2020_09_15_qualite_figure_4.png b/site/static/img/article_niveau_qualite/2020_09_15_qualite_figure_4.png deleted file mode 100644 index cd833c848..000000000 Binary files a/site/static/img/article_niveau_qualite/2020_09_15_qualite_figure_4.png and /dev/null differ diff --git a/site/static/img/article_niveau_qualite/2020_09_15_qualite_figure_5.png b/site/static/img/article_niveau_qualite/2020_09_15_qualite_figure_5.png deleted file mode 100644 index f442ab6a6..000000000 Binary files a/site/static/img/article_niveau_qualite/2020_09_15_qualite_figure_5.png and /dev/null differ diff --git a/site/static/img/article_niveau_qualite/2020_09_15_qualite_figure_6.png b/site/static/img/article_niveau_qualite/2020_09_15_qualite_figure_6.png deleted file mode 100644 index 922a386cc..000000000 Binary files a/site/static/img/article_niveau_qualite/2020_09_15_qualite_figure_6.png and /dev/null differ diff --git a/site/static/img/article_niveau_qualite/2020_09_15_qualite_figure_7.png b/site/static/img/article_niveau_qualite/2020_09_15_qualite_figure_7.png deleted file mode 100644 index 0edc2fd77..000000000 Binary files a/site/static/img/article_niveau_qualite/2020_09_15_qualite_figure_7.png and /dev/null differ diff --git a/site/static/img/article_niveau_qualite/2020_09_15_qualite_tableau_1.png b/site/static/img/article_niveau_qualite/2020_09_15_qualite_tableau_1.png deleted file mode 100644 index 4d05f1ad1..000000000 Binary files a/site/static/img/article_niveau_qualite/2020_09_15_qualite_tableau_1.png and /dev/null differ diff --git a/site/static/img/article_niveau_qualite/2020_09_15_qualite_tableau_2.png b/site/static/img/article_niveau_qualite/2020_09_15_qualite_tableau_2.png deleted file mode 100644 index ba1004ffd..000000000 Binary files a/site/static/img/article_niveau_qualite/2020_09_15_qualite_tableau_2.png and /dev/null differ diff --git a/site/static/img/article_uas_cybersecurity/2020_10_27_cybersecurity.jpg b/site/static/img/article_uas_cybersecurity/2020_10_27_cybersecurity.jpg deleted file mode 100644 index 8a329829d..000000000 Binary files a/site/static/img/article_uas_cybersecurity/2020_10_27_cybersecurity.jpg and /dev/null differ diff --git a/site/static/img/article_uas_cybersecurity/uas_cybersecurity_001.png b/site/static/img/article_uas_cybersecurity/uas_cybersecurity_001.png deleted file mode 100644 index 31ce7d772..000000000 Binary files a/site/static/img/article_uas_cybersecurity/uas_cybersecurity_001.png and /dev/null differ diff --git a/site/static/img/article_uas_cybersecurity/uas_cybersecurity_002.png b/site/static/img/article_uas_cybersecurity/uas_cybersecurity_002.png deleted file mode 100644 index 8ac175574..000000000 Binary files a/site/static/img/article_uas_cybersecurity/uas_cybersecurity_002.png and /dev/null differ diff --git a/site/static/img/article_uas_cybersecurity/uas_cybersecurity_003.png b/site/static/img/article_uas_cybersecurity/uas_cybersecurity_003.png deleted file mode 100644 index c3bcb38c1..000000000 Binary files a/site/static/img/article_uas_cybersecurity/uas_cybersecurity_003.png and /dev/null differ diff --git a/site/static/img/article_uas_cybersecurity/uas_cybersecurity_004.png b/site/static/img/article_uas_cybersecurity/uas_cybersecurity_004.png deleted file mode 100644 index b706f4b75..000000000 Binary files a/site/static/img/article_uas_cybersecurity/uas_cybersecurity_004.png and /dev/null differ diff --git a/site/static/img/article_uas_cybersecurity/uas_cybersecurity_005.png b/site/static/img/article_uas_cybersecurity/uas_cybersecurity_005.png deleted file mode 100644 index 910d63447..000000000 Binary files a/site/static/img/article_uas_cybersecurity/uas_cybersecurity_005.png and /dev/null differ diff --git a/site/static/img/article_uas_cybersecurity/uas_cybersecurity_006.png b/site/static/img/article_uas_cybersecurity/uas_cybersecurity_006.png deleted file mode 100644 index e14bc610b..000000000 Binary files a/site/static/img/article_uas_cybersecurity/uas_cybersecurity_006.png and /dev/null differ diff --git a/site/static/img/tumblr_inline_n2smvcq9H11szdlw2.png b/site/static/img/tumblr_inline_n2smvcq9H11szdlw2.png deleted file mode 100644 index 64e38da89..000000000 Binary files a/site/static/img/tumblr_inline_n2smvcq9H11szdlw2.png and /dev/null differ diff --git a/site/static/img/tumblr_inline_n2smvx5KGO1szdlw2.png b/site/static/img/tumblr_inline_n2smvx5KGO1szdlw2.png deleted file mode 100644 index 297adcdce..000000000 Binary files a/site/static/img/tumblr_inline_n2smvx5KGO1szdlw2.png and /dev/null differ diff --git a/site/static/img/tumblr_inline_n2sn4plGID1szdlw2.png b/site/static/img/tumblr_inline_n2sn4plGID1szdlw2.png deleted file mode 100644 index 89209bed5..000000000 Binary files a/site/static/img/tumblr_inline_n2sn4plGID1szdlw2.png and /dev/null differ diff --git a/site/static/img/tumblr_inline_n3b23pOjeJ1szbtlw.jpg b/site/static/img/tumblr_inline_n3b23pOjeJ1szbtlw.jpg deleted file mode 100644 index ac2d3ccd1..000000000 Binary files a/site/static/img/tumblr_inline_n3b23pOjeJ1szbtlw.jpg and /dev/null differ diff --git a/site/static/img/tumblr_inline_n3cvtoCAHW1szbtlw.jpg b/site/static/img/tumblr_inline_n3cvtoCAHW1szbtlw.jpg deleted file mode 100644 index 5d326c76f..000000000 Binary files a/site/static/img/tumblr_inline_n3cvtoCAHW1szbtlw.jpg and /dev/null differ diff --git a/site/static/img/tumblr_inline_n48g7nAIuG1sv6muh.jpg b/site/static/img/tumblr_inline_n48g7nAIuG1sv6muh.jpg deleted file mode 100644 index 3ee6ad4ac..000000000 Binary files a/site/static/img/tumblr_inline_n48g7nAIuG1sv6muh.jpg and /dev/null differ diff --git a/site/static/img/tumblr_inline_n48g88Z2E41sv6muh.png b/site/static/img/tumblr_inline_n48g88Z2E41sv6muh.png deleted file mode 100644 index 4b91c6bf7..000000000 Binary files a/site/static/img/tumblr_inline_n48g88Z2E41sv6muh.png and /dev/null differ diff --git a/site/static/img/tumblr_inline_n48ga47MGG1sv6muh.png b/site/static/img/tumblr_inline_n48ga47MGG1sv6muh.png deleted file mode 100644 index b76bd3e9a..000000000 Binary files a/site/static/img/tumblr_inline_n48ga47MGG1sv6muh.png and /dev/null differ diff --git a/site/static/img/tumblr_inline_n48gaeKJZ01sv6muh.png b/site/static/img/tumblr_inline_n48gaeKJZ01sv6muh.png deleted file mode 100644 index 6e46d889a..000000000 Binary files a/site/static/img/tumblr_inline_n48gaeKJZ01sv6muh.png and /dev/null differ diff --git a/site/static/img/tumblr_inline_n48gawrFT61sv6muh.png b/site/static/img/tumblr_inline_n48gawrFT61sv6muh.png deleted file mode 100644 index 09a01fff1..000000000 Binary files a/site/static/img/tumblr_inline_n48gawrFT61sv6muh.png and /dev/null differ diff --git a/site/static/img/tumblr_inline_n48gbdabr71sv6muh.png b/site/static/img/tumblr_inline_n48gbdabr71sv6muh.png deleted file mode 100644 index 966db19d6..000000000 Binary files a/site/static/img/tumblr_inline_n48gbdabr71sv6muh.png and /dev/null differ diff --git a/site/static/img/tumblr_inline_n48gbsuxvD1sv6muh.png b/site/static/img/tumblr_inline_n48gbsuxvD1sv6muh.png deleted file mode 100644 index 4eefe27e8..000000000 Binary files a/site/static/img/tumblr_inline_n48gbsuxvD1sv6muh.png and /dev/null differ diff --git a/site/static/img/tumblr_inline_n48gchB3dB1sv6muh.png b/site/static/img/tumblr_inline_n48gchB3dB1sv6muh.png deleted file mode 100644 index af98a4779..000000000 Binary files a/site/static/img/tumblr_inline_n48gchB3dB1sv6muh.png and /dev/null differ diff --git a/site/static/img/tumblr_inline_n48gcutXVJ1sv6muh.png b/site/static/img/tumblr_inline_n48gcutXVJ1sv6muh.png deleted file mode 100644 index 22933ad97..000000000 Binary files a/site/static/img/tumblr_inline_n48gcutXVJ1sv6muh.png and /dev/null differ diff --git a/site/static/img/tumblr_inline_n48gd62cYl1sv6muh.png b/site/static/img/tumblr_inline_n48gd62cYl1sv6muh.png deleted file mode 100644 index 6c59f45f1..000000000 Binary files a/site/static/img/tumblr_inline_n48gd62cYl1sv6muh.png and /dev/null differ diff --git a/site/static/img/tumblr_inline_n48gdd2NyQ1sv6muh.png b/site/static/img/tumblr_inline_n48gdd2NyQ1sv6muh.png deleted file mode 100644 index 3f3bdd931..000000000 Binary files a/site/static/img/tumblr_inline_n48gdd2NyQ1sv6muh.png and /dev/null differ diff --git a/site/static/img/tumblr_inline_n48gdo5kkP1sv6muh.png b/site/static/img/tumblr_inline_n48gdo5kkP1sv6muh.png deleted file mode 100644 index 681d1f8ab..000000000 Binary files a/site/static/img/tumblr_inline_n48gdo5kkP1sv6muh.png and /dev/null differ diff --git a/site/static/img/tumblr_inline_n48gdvtyai1sv6muh.png b/site/static/img/tumblr_inline_n48gdvtyai1sv6muh.png deleted file mode 100644 index 7f54c4ff2..000000000 Binary files a/site/static/img/tumblr_inline_n48gdvtyai1sv6muh.png and /dev/null differ diff --git a/site/static/img/tumblr_inline_n48gegRF0Y1sv6muh.png b/site/static/img/tumblr_inline_n48gegRF0Y1sv6muh.png deleted file mode 100644 index 7f54c4ff2..000000000 Binary files a/site/static/img/tumblr_inline_n48gegRF0Y1sv6muh.png and /dev/null differ diff --git a/site/static/img/tumblr_inline_n48ges1Zyn1sv6muh.png b/site/static/img/tumblr_inline_n48ges1Zyn1sv6muh.png deleted file mode 100644 index 056c60ef8..000000000 Binary files a/site/static/img/tumblr_inline_n48ges1Zyn1sv6muh.png and /dev/null differ diff --git a/site/static/img/tumblr_inline_n48gf2wObf1sv6muh.png b/site/static/img/tumblr_inline_n48gf2wObf1sv6muh.png deleted file mode 100644 index f56a1282c..000000000 Binary files a/site/static/img/tumblr_inline_n48gf2wObf1sv6muh.png and /dev/null differ diff --git a/site/static/img/tumblr_inline_n48ggyrfGl1sv6muh.png b/site/static/img/tumblr_inline_n48ggyrfGl1sv6muh.png deleted file mode 100644 index 11337f67c..000000000 Binary files a/site/static/img/tumblr_inline_n48ggyrfGl1sv6muh.png and /dev/null differ diff --git a/site/static/img/tumblr_inline_n4flfbB8PI1sv6muh.png b/site/static/img/tumblr_inline_n4flfbB8PI1sv6muh.png deleted file mode 100644 index c823955c6..000000000 Binary files a/site/static/img/tumblr_inline_n4flfbB8PI1sv6muh.png and /dev/null differ diff --git a/site/static/img/tumblr_inline_n4fqp7Lh6w1sv6muh.png b/site/static/img/tumblr_inline_n4fqp7Lh6w1sv6muh.png deleted file mode 100644 index f2e6ab9b5..000000000 Binary files a/site/static/img/tumblr_inline_n4fqp7Lh6w1sv6muh.png and /dev/null differ diff --git a/site/static/manifest.json b/site/static/manifest.json deleted file mode 100644 index c5dcdc34e..000000000 --- a/site/static/manifest.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "name": "", - "icons": [ - { - "src": "/img/android-chrome-192x192.png", - "sizes": "192x192", - "type": "image/png" - }, - { - "src": "/img/android-chrome-512x512.png", - "sizes": "512x512", - "type": "image/png" - } - ], - "theme_color": "#ffffff", - "display": "standalone" -} diff --git a/site/themes/sogilis/LICENSE b/site/themes/sogilis/LICENSE deleted file mode 100644 index 6323b4607..000000000 --- a/site/themes/sogilis/LICENSE +++ /dev/null @@ -1,20 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2020 SOGILIS - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/site/themes/sogilis/archetypes/default.md b/site/themes/sogilis/archetypes/default.md deleted file mode 100644 index ac36e0622..000000000 --- a/site/themes/sogilis/archetypes/default.md +++ /dev/null @@ -1,2 +0,0 @@ -+++ -+++ diff --git a/site/themes/sogilis/assets/css/article.css b/site/themes/sogilis/assets/css/article.css deleted file mode 100644 index a4176117f..000000000 --- a/site/themes/sogilis/assets/css/article.css +++ /dev/null @@ -1,285 +0,0 @@ -/* NOTE: INTO HUGO, THE FOLLOWING CSS DEFINITION CORRESPOND TO THE FILE SINGLE.HTML */ -/* =========================================================================== */ -/* TODO remove preceding comment when we will migrate to another tool */ - -#articlepage { - margin-bottom: 5rem; - display: flex; - flex-direction: column; - max-width: inherit; - color: var(--color-black-mine-shaft); - font-size: 16px; - line-height: 1.5; -} - -/* Folliwng copied from Bootstrap 4.4.1 */ -/* following is not into reboot.css */ -/* ========================================================================== */ - -code { - /* Line 503 into bootstrap.css 4.4.1 */ - font-size: 87.5%; - color: #e83e8c; - word-wrap: break-word; -} - -a > code { - color: inherit; -} - -kbd { - padding: 0.2rem 0.4rem; - font-size: 87.5%; - color: #fff; - background-color: #212529; - border-radius: 0.2rem; -} - -kbd kbd { - padding: 0; - font-size: 100%; - font-weight: 700; -} - -pre { - display: block; - /* Redefined by use bellow */ - /* color: #212529; */ -} - -pre code { - color: inherit; - word-break: normal; -} - -/* Definitions for elements in the page article */ -/* ========================================================================== */ - -pre, -code, -kbd, -samp { - max-width: 100%; - font-family: var(--font-jetbrains-mono); - font-size: 1em; -} - -h2, -h3, -h4, -h5, -h6 { - margin: 1.5em 0; -} - -blockquote, -pre, -.articlepage-img { - box-shadow: 0 0 1rem 0.2rem rgba(0, 0, 0, 0.15); - width: 100%; -} - -blockquote, -pre { - margin: 2rem 0; - padding: 0.4rem 1.25rem; - border: solid 5px transparent; -} - -@media (max-width: 991px) { - blockquote, - pre { - width: inherit; - margin: 1.9rem 0 1.9rem 1.5rem; - } -} -@media (max-width: 768px) { - blockquote, - pre { - margin-left: 0; - } -} - -blockquote { - font-style: italic; - border-left-color: var(--dark-blue); - background-color: white; -} - -pre { - border-left-color: var(--medium-blue); -} - -/* Reduce caption margin in a
(instead of default h4 margin) */ -/* Generated with {{< figure …>}} in .md files */ -figcaption h4 { - margin-top: 0.5rem; -} - -#articlepage { - max-width: 42.5rem; - margin: 0 auto; -} - -#articlepage-content a { - color: var(--wolf-blue); - transition: color 0.3s; -} - -#articlepage-content a:hover { - color: var(--dark-grey); -} - -#articlepage p { - margin-bottom: 1.5em; - hyphens: auto; -} - -/* Section header */ -/* ========================================================================== */ -#articlepage-header { - display: flex; - flex-direction: column; - margin-bottom: 3rem; - word-wrap: break-word; -} - -#articlepage-header-img-container { - width: 100%; - margin-bottom: 1em; -} - -@media (min-width: 992px) { - #articlepage-header { - flex-direction: row; - } - - #articlepage-header-img-container { - width: 70%; - margin-bottom: 0; - } -} - -.articlepage-header-title-details { - margin-left: 1.6rem; -} - -/* Section content */ -/* ========================================================================== */ -#articlepage-content { - margin-bottom: 1em; -} -/* ========================================================================== */ -#articlepage-content img { - display: block; - max-width: 100%; - margin: 0 auto; -} -#articlepage-content figure img { - max-width: auto; - margin: 0; -} - -/* Image on the bottom */ -/* ========================================================================== */ -@media (min-width: 768px) { - #articlepage-img-bottom { - max-width: 50%; - margin: 0 auto; - } -} - -/* Section comment */ -/* ========================================================================== */ - -#articlepage-comments { - margin-top: 3rem; -} - -/* Code font */ -/* ========================================================================== */ - ---font-jetbrains-mono: 'JetBrainsMono'; - -@font-face { - font-family: var(--font-jetbrains-mono); - src: url('/fonts/JetBrainsMono/woff2/JetBrainsMono-Bold-Italic.woff2') - format('woff2'), - url('/fonts/JetBrainsMono/woff/JetBrainsMono-Bold-Italic.woff') - format('woff'); - font-weight: 700; - font-style: italic; - font-display: swap; -} - -@font-face { - font-family: var(--font-jetbrains-mono); - src: url('/fonts/JetBrainsMono/woff2/JetBrainsMono-Bold.woff2') - format('woff2'), - url('/fonts/JetBrainsMono/woff/JetBrainsMono-Bold.woff') format('woff'); - font-weight: 700; - font-style: normal; - font-display: swap; -} - -@font-face { - font-family: var(--font-jetbrains-mono); - src: url('/fonts/JetBrainsMono/woff2/JetBrainsMono-ExtraBold-Italic.woff2') - format('woff2'), - url('/fonts/JetBrainsMono/woff/JetBrainsMono-ExtraBold-Italic.woff') - format('woff'); - font-weight: 800; - font-style: italic; - font-display: swap; -} - -@font-face { - font-family: var(--font-jetbrains-mono); - src: url('/fonts/JetBrainsMono/woff2/JetBrainsMono-ExtraBold.woff2') - format('woff2'), - url('/fonts/JetBrainsMono/woff/JetBrainsMono-ExtraBold.woff') format('woff'); - font-weight: 800; - font-style: normal; - font-display: swap; -} - -@font-face { - font-family: var(--font-jetbrains-mono); - src: url('/fonts/JetBrainsMono/woff2/JetBrainsMono-Italic.woff2') - format('woff2'), - url('/fonts/JetBrainsMono/woff/JetBrainsMono-Italic.woff') format('woff'); - font-weight: 400; - font-style: italic; - font-display: swap; -} - -@font-face { - font-family: var(--font-jetbrains-mono); - src: url('/fonts/JetBrainsMono/woff2/JetBrainsMono-Medium-Italic.woff2') - format('woff2'), - url('/fonts/JetBrainsMono/woff/JetBrainsMono-Medium-Italic.woff') - format('woff'); - font-weight: 500; - font-style: italic; - font-display: swap; -} - -@font-face { - font-family: var(--font-jetbrains-mono); - src: url('/fonts/JetBrainsMono/woff2/JetBrainsMono-Medium.woff2') - format('woff2'), - url('/fonts/JetBrainsMono/woff/JetBrainsMono-Medium.woff') format('woff'); - font-weight: 500; - font-style: normal; - font-display: swap; -} - -@font-face { - font-family: var(--font-jetbrains-mono); - src: url('/fonts/JetBrainsMono/woff2/JetBrainsMono-Regular.woff2') - format('woff2'), - url('/fonts/JetBrainsMono/woff/JetBrainsMono-Regular.woff') format('woff'); - font-weight: 400; - font-style: normal; - font-display: swap; -} diff --git a/site/themes/sogilis/assets/css/header-inf767.css b/site/themes/sogilis/assets/css/header-inf767.css deleted file mode 100644 index 8b96d9723..000000000 --- a/site/themes/sogilis/assets/css/header-inf767.css +++ /dev/null @@ -1,272 +0,0 @@ -/* - * In this screen size, we have three tags child - * On the top left, the icon of Sogilis - * On the top right, a button with three bar. When we click on it - * this button is transormed to a cross and the nav button appears. - * When we click on the top right button, the tag