- Table of content
- Development process
- pnpm
- Linting
- Continuous integration / deployment
- Project structure
- Testing
- Environment variables
- Tooling to update translation
- Project deployment
This project uses a git flow approach.
Meaning all changes should be made through a PR to the develop
branch.
It uses the SemVer 2.0 versioning scheme, which is automatically handled by
a github action script. Each PR merged into develop
creates a new beta version. While a version
is made by creating a PR to merge develop
into master
after tests are green (Test campaign TBD).
All commits on master
thus represent a new version of the app.
When merging on master
(from a develop
branch), an automatic git tag will be added to
master
branch by github-tag-bump
This tag will be a bump in :
- major version (vx.0.0) if one commit message in the PR contains the
#major
word (with the hash) - a patch version (v0.0.x) if one commit message in the PR contains the
#patch
word - minor version (v0.x.0) if none of the above is true and merge is on
master
, or if a commit message of the PR contains the word#minor
- beta version (v0.0.0-beta.x) if none of the above is true
This project uses pnpm
instead of npm
. The cli commands are more or less the same.
The main difference is that new packages need to be specified in the global pnpm-workspace.yaml
file.
The subpackages will then use the same versions of the dependencies across the entire codebase.
command | what it does |
---|---|
pnpm run dev |
Compiles and hot-reloads for development. Will serve the project under http://localhost:8080 (or the next available port if 8080 is already used, see console output). |
pnpm run build-all |
Compiles all file without bundling and minification |
pnpm run build:(dev|int|prod) |
Compiles all file for the according mode |
pnpm run lint |
Format, lints and fixes |
pnpm run lint:no-fix |
Check formatting and linting without auto fixes |
pnpm run test:unit |
Runs unit tests from vitest. |
pnpm run test:unit:watch |
Runs unit tests and then watch for changes, re-running any part of the tests that is edited (or tests linked to parts of the app that has changed). |
pnpm run test:e2e |
Opens up the cypress app with a mobile sized view |
pnpm run test:e2e:headless |
Run cypress E2E tests in headless mode with a mobile sized view |
pnpm run test:e2e:tablet |
Opens up the cypress app with a iPad sized view |
pnpm run test:e2e:desktop |
Opens up the cypress app with a 1080p sized view |
pnpm run test:e2e:ci |
Run cypress E2E tests on the served URL (NOTE: the server should be started before). Only tests the mobile sized view. |
pnpm run test:component |
Opens up the cypress component tests |
pnpm run test:component:headless |
Run cypress component tests in headless mode |
pnpm run test:component:ci |
Run cypress component tests |
pnpm run update:translations |
Update translation files according to our Google Spreadsheet. See above for required tools. |
All scripts commands starting a webserver or using one (dev
and all things related to cypress) will determine the port to use by looking for the next one available starting at 8080
.
The CI uses this file to ensure it will not stumble upon a minor version of a library that breaks the app. So this file needs to be versioned, and kept up to date (each time a new library or version of a library is added to package.json
, pnpm install
will update pnpm-lock.yaml
accordingly).
This project uses ESLint as the main linter, and uses presets from https://prettier.io/
Lint is enforced at each build, and will result in an error if something is wrong. You can auto lint
your code by running pnpm run lint
(this will lint the whole src/
folder)
As we are using ESLint out of the box, most modern IDE will read ESLint config file eslint.config.mjs
and incorporate it to the environment. It is advised to deactivate code generation (or fix it in the
settings) for code parts that are not compliant with our linting policy (lambda declaration, auto
semi-column, etc...).
CI is managed by AWS CodeBuild.
- Every merge (commit) on
develop
will trigger a deploy on https://sys-map.dev.bgdi.ch/ by the CI - Every merge (commit) on
master
will trigger a deploy on https://sys-map.int.bgdi.ch/ by the CI
This is a Vue app that is served through src/main.js
, using Vuex as a state manager.
The app is divided into packages (or modules) that are stored into packages/
. The goal is for each of these modules to be able to be externalized if needed.
They are using PNPM workspaces to be linked together, making this repo a sort of monorepo.
To make the code easier to navigate and maintain, we consolidated the complete state of the viewer in one place (packages/mapviewer/src/store/
).
The store is divided into modules that mostly correspond to the application parts but also include modules for state that is used by multiple parts of the app or would be too big for a single file.
The goal is to have a centralized way of dealing with changes, and not delegate that to each component.
Store plugins can be used to react to store changes. See the store read-me for more information.
- Prefer primitive data or javascript plain object in reactive data (Vue Component data or refs, Vuex store data)
- Don't use a complex object as reactive data
- Avoid using JavaScript getter and setter in class that are used in reactive data
See also Store Best Practices
New components should be written using the Vue Composition API.
The structure of the file should be :
<script setup>
tag should be the first tag of the.vue
file (instead of<template>
, that's the new best practice with this approach)- declares things in this order in the
<script setup>
tag- imports
- props (input)
- data
- store mapping (input)
- computed (transformation of inputs)
- watchs
- life-cycle hooks (mounted and such)
- interaction with the user (was called
methods
in the OptionAPI)
<script setup>
// 1. First put the imports
import { computed, onMounted, ref, watch } from 'vue'
import { useStore } from 'vuex'
// 2. Put all the props (input)
const { myProp } = defineProps({
myProp: {
type: Boolean,
default: false,
},
})
// 3. reactive data
const myData = ref('My reactive value')
// 4. Put then all store mapping (input)
const store = useStore()
// 5. Computed properties
const myComputed = computed(() => store.state.myValue)
// 6. Watchs
watch(myComputed, (newValue) => {
// do something on myComputed changes
})
// 7. Life-cycle hooks
onMounted(() => {
// write you code here
})
// 8. Methods
function myMethod() {}
</script>
<template>
<!-- Write your template here -->
</template>
<style lang="scss" scoped>
// Write your styles here
</style>
Components that are extensively edited should be rewritten using the Composition API
As there can be only one instance of a Vuex's store per app, the store module is there for that. It as the responsibility to instantiate Vuex, and add any module related state data to the store. See its README.md for more details.
Unit testing is done through the VueCLI unit test helper, and integration testing is done with Cypress.io.
All things related to tests are in each packages /tests
folder.
See README.md for more documentation on testing in this project.
Environment variables are defined in the following files
- .env.development
- .env.integration
- .env.prodcution
The first one is used by pnpm run dev
as well as for all development
modes. The second is used to build for and deploy to our integration server. Otherwise .env.production
is used by default.
For more information about loading environment variables see Vue - Modes and Environment Variables
Our translation master is hosted in a Google Spreadsheet, thus if you want to update translations you will need a valid Google API Key.
One can be found in our AWS SSM store in swisstopo-bgdi-builder
account.
In order to easily access the google API key stored in AWS SSM we use summon as follow:
Translations can then be updated with
summon -p ssm pnpm run update:translations
The file secrets.yml
will tell summon
which keys to get from AWS SSM.
The application is deployed on three targets : dev|int|prod
After every successful build triggered by a merge into develop
, a version is automatically deployed in DEV staging. After every successful build triggered by a a merge into master
, a version is automatically deployed on INT and PROD staging.
automatically.
environment | hostname | path | branch |
---|---|---|---|
PR | sys-map.dev.bgdi.ch | /preview/<branch_name> | <bug-/feat-> |
dev | sys-map.dev.bgdi.ch | / | develop |
int | sys-map.int.bgdi.ch | / | master |
prod | sys-map.prod.bgdi.ch | / | master |
The deployments are done automatically via the CI for web-mapviewer.
A test link is also added to the description of every PR automatically using github workflow.
A bash script deploy.sh is used for manual deploy, either from a local directory or a bucket from the CI.
./scripts/deploy.sh: --staging STAGING {--version VERSION | --local-src DIR} [--preview TEST_LINK]
Deploy web-mapviewer on the given staging. Either deploy a version from the
build-artifacts-swisstopo bucket (with --version option), or a local build version
using the --local-src DIR option.
OPTIONS:
-h|--help Print the help and exit.
-s|--staging STAGING Staging to deploy; dev|int|prod. Default; dev
-v|--version VERSION Version to deploy.
On prod
, check deploy on prod and use the script from within infra-terraform-bgdi-builder/projects/web_mapviewer
to deploy manually.
NOTE:
If deploying manually toprod
, wait until the CI has finished building the project, as the deploy script only copy files.
Depending on the target (dev|int|prod
), you will have to build and bundle/minify the app (for int
and prod
) or simply build the app without minification (for dev
) prior to deplay (pnpm run build:(dev|int|prod)
)
- Only
develop
branch can be deployed at the root of thedev
bucket. - Only
master
branch can be deployed at the root ofint
andprod
buckets.