README.md 19.9 KB
Newer Older
Manuel Tancoigne's avatar
Manuel Tancoigne committed
1
# Readme
2

3
4
- [French instance](https://garden-party.experimentslabs.com),
  [English instance](https://app.garden-party.io)(lower maintenance rate)
5
- [User documentation](https://garden-party.io)
6
7
8
- [Matrix chatrooms](https://app.element.io/#/room/#garden-party:matrix.org)
  - [general discussion](https://matrix.to/#/#gp:matrix.org)
  - [development/technical discussions](https://matrix.to/#/#gp-dev:matrix.org)
9
10
11
- [Decision board](https://framavox.org/garden-party/) (Lumio on Framavox; create an account _before_ joining the group)

We're mostly francophones but we can handle english discussions.
12

Manuel Tancoigne's avatar
Manuel Tancoigne committed
13
## Getting started
14

15
16
17
18
### Pre-requisites

You will need a server with the following executable/libraries

19
20
- `svgo`, version 1.3.2 (needed by `image_optim` gem, for cleaning
  SVGs). Version 2 is not yet supported by image_optim.
21
22
23
24
25
26
- We use [Vips](https://github.com/libvips/ruby-vips) to process images.
  (`libvips42` on debian-based distributions).

**Development requirements**

- `libpq-dev` (needed by `pg` gem, for database access)
27

28
29
30
31
32
33
34
### Install dependencies

```sh
bundle install
yarn install
```

Manuel Tancoigne's avatar
Manuel Tancoigne committed
35
### Database configuration
36

Manuel Tancoigne's avatar
Manuel Tancoigne committed
37
We use PostgreSQL for this project.
38

39
40
To get started, copy the configuration files from their defaults and
modify them to match your setup.
41

Manuel Tancoigne's avatar
Manuel Tancoigne committed
42
43
```sh
cp config/database.default.yml config/database.yml
44
cp config/garden_party_defaults.yml config/garden_party_instance.yml
Manuel Tancoigne's avatar
Manuel Tancoigne committed
45
```
46

47
Prepare the _credentials_ file: run `bundle exec rails credentials:edit`
48
and complete with content from `config/credentials.default.yml`.
49
50
This command generates `config/master.key` and `config/credential.yml.enc`.

51
**These two files should not be versioned** but must be deployed in
52
53
54
55
56
production.

`config/credential.yml.enc` contains secrets as the salt used for
passwords and secure cookies.

Manuel Tancoigne's avatar
Manuel Tancoigne committed
57
Create database and run the migrations and seeds:
58

Manuel Tancoigne's avatar
Manuel Tancoigne committed
59
60
61
62
63
```sh
bundle exec rails db:create
bundle exec rails db:migrate
bundle exec rails db:seed
```
64

65
If you need to drop the database:
66

Manuel Tancoigne's avatar
Manuel Tancoigne committed
67
68
69
```sh
bundle exec rails db:drop
```
70

71
## Production
72

73
74
Feel free to expand this section if you encounter any issue with
deployment; it's a basic Rails application with no specific things.
75

76
77
78
79
## Updates

To update Garden Party, follow the [migration guide](MIGRATION_GUIDE.md).

80
81
82
83
84
85
86
## Instance maintenance

### Synchronize library with a data source

An instance can be bound to a reference data source to easily get new data or
complete existing library entries.

87
[A repository exists](https://gitlab.com/experimentslabs/garden-party/data) with various packs of data
88
89
90
91
92

To define the datasource, fill `trusted_datasource_url` in `config/garden_party_instance.yml`
with the URL to the desired pack.

Note that this _should_ be done as soon as possible to avoid maintenance with an
93
existing library, and even if changing the source is possible with a little
94
95
fiddle in Rails console, it is discouraged.

96
Admins will find an "import" link in the main menu which leads to the
97
98
99
synchronization pages: import new elements in library or update existing entries.
The process can even be done field by field.

100
To import a large amount of new entries, use the rake task (it won't update
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
existing entries, nor change existing entries with same names):

```shell
rake data:import_new
```

### Contribute to a pack

If you want to contribute to the pack you use, you can easily export data from
your instance and complete the pack with a merge request.

```shell
rake data:export
```

116
117
118
119
120
121
122
## Useful rake tasks

- `stats:show` - Displays statistics about the instance.
- `resources:reset_colors` - Reset resource colors based on their name.

Other tasks are described in their own sections.

123
124
125
## Licences

- The code is under the [MIT](LICENCE) license.
126
- Seeds data comes from
127
  [Garden Party Data repository](https://gitlab.com/experimentslabs/garden-party/data),
128
129
130
131
132
133
  and is under the [CC-0](LICENCE_CC0) license.
- Images in `app/assets/images` and `src/icons` are crafted with love for Garden
  Party, under the [CC-0](LICENCE_CC0). Exception is made for
  `app/assets/images/icons.svg` which is a generated compilation of symbols from
  [PhosphorIcons](https://github.com/phosphor-icons/phosphor-home) and Garden
  Party specific symbols. PhosphorIcons symbols are under the MIT licence.
134
135
  - `app/assets/images/osm_map.svg` is based on [a file from Wikimedia](https://commons.wikimedia.org/wiki/File:Shokunin_world_map_more_detail.svg),
   under the [CC-0](LICENCE_CC0) license.
136

Manuel Tancoigne's avatar
Manuel Tancoigne committed
137
138
## Development

139
140
141
142
143
144
145
146
147
148
149
Before the first run in development, you need to generate the JS locales:

```sh
rake i18n:js:export
```

There is no need to run it in production as the generation is handled by
`assets:precompile`.

Once the locales are generated you're ready to go.

150
151
152
153
Start both the rails server and the webpack development server in two
separate terminals:

```sh
154
rails server # or "rails s"
155
156
157
bin/webpack-dev-server
```

158
**Note**: The javascript locales are generated on page reload, which means
159
they will always be out of sync unless you re-generate them manually or reload
160
the page twice.
161
162
163
164
165
166
167
168
169
170
171
172

### Useful rake tasks

Tasks that (re)generate some JS files during development

- `js:generate` - generates javascript models and vuex modules
- `js:generate_models` - generates javascript models
- `js:generate_modules` - generates javascript vuex modules
- `js:generate_factories` - generates javascript fishery factories
- `js:generate_factories_test` - generates js factories test file. The output file is not committed but the CI runs it

Other tasks
173

174
- `uml:models` - outputs PlantUML diagram of tables and relations
175
176
177

Other tasks are described in their own sections.

178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
### Icons

The project uses icons from various packages. For now, it's :

- [Phosphor icons](https://github.com/phosphor-icons/phosphor-home),
  package defined in `package.json`
- Custom icons, from `src/icons`. They try to match the overall phosphor
  design

As that represents a lot of icons, the ones used are defined in
`[config/icons.yml](config/icons.yml)`. Use the following rake task to
generate the icon file:

```sh
rake icons:generate
```

It creates a SVG file with icons accessible by their identifiers.

197
198
199
200
201
202
203
204
205
206
207
208
209
### GeoJSON features

Patches represents areas in the garden; they have a `geometry` attribute
corresponding to the shape and position of the patch (`Point` or
`Polygon`). As we want to represent circles on the map, we add a `radius`
property to a `Point` geoJSON object.

Compared to a full geoJSON `Feature`, we don't support bounding boxes on
any property, so setting one will trigger a validation error.

Additionally, no other property than `radius` are allowed, triggering
validation errors.

Manuel Tancoigne's avatar
Manuel Tancoigne committed
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
### FactoryBot - [site](https://github.com/thoughtbot/factory_bot)

FactoryBot Rails allows to quickly create entities. It's useful in
tests, as creating fixtures can be a real pain.

They are located in `spec/factories`, and are available in Rails
console, RSpec and Cucumber. It's also used for development seeds.

Note that FactoryBot is not available in production.

FactoryBot is configured to create related entities by default, instead
of only building them (`FactoryBot.use_parent_strategy = false`)

#### Check the factories

A rake task can be used to test the ability to run a factory multiple
times:

```sh
rake factory_bot:lint
```

This task is executed in CI

### Faker - [site](https://github.com/faker-ruby/faker)

Faker is used during development to generate fake data. It should be
used in new factories.

### Authentication

We use [Devise](https://github.com/plataformatec/devise) for
authentication.

These FactoryBot factories will help you during the development:

- `user` for a random user
- `user_known` for an user with email "user@example.com"
- `user_admin` for an admin
- `user_admin_known` for an admin with email "admin@example.com"

All created user are created with the `password` password unless
you specify a custom one.

### Authorization

Authorization is managed with
[Pundit](https://github.com/varvet/pundit).

Usage of `authorize xxx` and `policy_scope` are not enforced in
controllers, but in RSpec tests (check `spec/rails_helper.rb`).

### Roles namespaces

Roles have their own controller namespaces (while models have not):

- `app/controllers` is for public controllers
- `app/controllers/admin` is for admins

### Internationalization

Internationalization is made as in "traditional" Rails applications, and
is managed with [i18n-tasks](https://github.com/glebm/i18n-tasks)

`i18n-tasks` helps to check if your translations are up-to-date.

A custom rake task exists to add missing translations, prefixed with
`TRANSLATE_ME` to find them easily, and another one adds the model
attributes in a dummy file, so they can be translated.

```sh
# Check
i18n-tasks health
# Add missing model attributes in `app/i18n/model_attributes.i18n`
rake i18n:add-models-attributes
# Add missing translations
rake i18n:add-missing
# Remove unused translations
i18n-tasks remove-unused
```

Check [config/i18n-tasks.yml](config/i18n-tasks.yml) for configuration.

An RSpec test checks for missing/unused translations, and a CI job tests
for missing model attributes translations.

296
297
298
299
300
While the site structure is translated in english and other languages,
the development seeds are in french. It's up to you to provide localized
seeds; maybe a file like `db/seeds.yml` for your locale; we'll sort this
out how it can be integrated.

301
302
303
304
305
306
#### Javascript

Thanks to [i18n-js](http://rubygems.org/gems/i18n-js), scripts can use
an `I18n` global to translate strings.

The only keys exported are `js.*` and `generic.*`. Check
307
[config/i18n-js.yml](/config/i18n-js.yml) for configuration.
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328

### Haml views - [site](http://haml.info/)

This project uses HAML views, except for text emails.

To help having a consistent formatting, `haml_lint` checks files in CI.

To run it, simply execute:

```sh
haml-lint
```

### VueJS

This project uses VueJS.

The VueJS files should be organized this way:

```text
app/javascript/
329
330
331
├── channels/        # Not used yet (ActionCable)
├── helpers/         # JS helpers, unrelated to a particular pack
├── jest_utils/      # Jest configuration and custom loaders
332
├── entrypoints/         # Application entrypoints to use with "vite_javascript_tag"
333
334
335
│   ├── locales/         # Locales, generated during asset compilation
│   ├── vue-app1.js      # App 1
│   ├── vue-app2.js      # ...
336
│   └── ...
337
└── vue/             # Vue applications and components
338
    ├── app_helpers/     # Helpers for Vue applications only
339
340
341
342
343
344
    ├── apps/            # Applications
    │   └── app1/
    │       ├── components    # Application-related components. Check below
    │       ├── mixins/       # Mixins, if any
    │       ├── App.vue       # Entry point to load in pack
    │       ├── router.js     # Router, if needed
345
    │       └── store.js      # VueX store, if needed
346
    ├── assets/          # Assets used only in Vue apps, that should be bundled
347
348
    ├── classes/         # Instantiable classes. Vue/VueX/... dependents
    │   └── models/          # Models, related to Rails models
349
350
351
352
    ├── common/          # Common components. Check below
    ├── stores/          # VueX modules
    ├── tools/           # Helpers. May be merged with "app_helper" some day
    └── widgets/         # Tiny vue apps used in some Rails views as components
353
354
```

355
356
357
358
359
360
#### Common components

A common component is generic enough to be used in many Vue applications, they
may be seen as an external component library. Since they are the base of all
the Vue apps, they should be documented with a storybook file.

361
For readability and easy edition, components have their stylesheets next to
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
them.

These components can be previewed with `yarn storybook`.

A basic component has the following structure:

```txt
MyComponent/
├── MyComponent.scss
├── MyComponent.stories.scss
└── MyComponent.vue
```

If the component have related components (e.g.: `Menu` and `MenuItem`), they can
fit in the same directory.

They follow the these conventions:

- the file names matches the directory name
- the component's `name` matches the file name, prefixed with `Gp` (i.e.: `GpMainMenu`)
- the component's main CSS class is prefixed with `gp-` (i.e.: `gp-main-menu`)
- the style is _not_ defined in the `.vue` file (no `<style>` tag). The style is
  included in the global stylesheet and CSS classes are available for usage in
  Rails views

Should you create a common component or an application one ?

- My component is small and could be reused in another project? **Common component**
390
391
392
- My component does not need to make API calls or fetch VueX data? It could be
  a **common component**
- My component does not use many components to display itself? Certainly a
393
394
395
396
  **common component**

#### Application components

397
Application components are, as the name suggest, components designed for an
398
399
400
401
402
403
application only. They often use many components to build a complete chunk of
interface.

They follow these conventions:

- the file names matches the directory name
404
- the component's `name` matches the file name, prefixed with `Gpa` (i.e.:
405
406
  `GpaLibraryPanel`)
- the component's main CSS class is prefixed with `gpa-` (i.e.: `gpa-library-panel`)
407
- the style _is_ defined in the `.vue` file:
408
409
410
  `<style lang="scss" src="./LibraryPanel.scss">`.
  This way the style is excluded from the global stylesheet and loaded on-demand.

411
412
### Style

413
414
415
416
417
418
419
420
421
422
423
424
425
Stylesheets are located in various locations depending on their scope; this may
be confusing but let us keep things separated and identified:

- Common components (`.gp-*` classes) are next to their components
  in `app/javascript/vue/common`
- Application components (`.gpa-*` classes) are next to their components
  in `app/javascript/vue/apps`
- html components (`._*` classes) are in `app/javascript/stylesheets/components`
  . These are classes that don't belong to a VueJS component
- helpers (`.-*` classes) are in `app/javascript/stylesheets/helpers`. These are
  classes which changes only one thing
- reset, CSS variables, animations, SCSS mixins and placeholders are
  in `app/javascript/stylesheets` sub directories
426

Manuel Tancoigne's avatar
Manuel Tancoigne committed
427
428
429
430
431
432
433
434
435
436
437
438
### Seeds

There are 3 seeds files available in the project:

- `db/seeds_development.rb`
- `db/seeds_production.rb`
- `db/seeds.rb` for shared seeds.

When you seed the database with `rails db:seed`, shared seeds are run
first, then one of the other files is executed, depending on the
environment.

439
As the seeds are extracted from the
440
441
442
443
[Garden Party Data repository](https://gitlab.com/experimentslabs/garden-party/data),
you better contribute to this project as it is used as a trusted data source
for instances synchronisation.

Manuel Tancoigne's avatar
Manuel Tancoigne committed
444
445
446
447
448
### Continuous integration

CI jobs are configured for Gitlab. Check
`[.gitlab-ci.yml](.gitlab-ci.yml)` to see the list.

449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
### Writing documentation

The
[official documentation repo](https://gitlab.com/experimentslabs/garden-party-docs/)
is on Gitlab. When you create a feature that needs to be documented,
documentation _should_ be updated too.

Creating screenshots is a cumbersome experience, so there is a Cucumber
profile to generate them:

```shell
bundle exec cucumber --profile documentation
```

Screenshots will be saved in `tmp/capybara_screenshots`, with the current
locale appended to the screenshot name, so you'll have to generate them
for every language manually. It still better than doing it by hand...

Other documentation details are specified
[in its own README](https://gitlab.com/experimentslabs/garden-party-docs/-/blob/next/README.md).

Manuel Tancoigne's avatar
Manuel Tancoigne committed
470
471
## Testing

472
473
474
475
476
477
### Overcommit

[Overcommit](https://github.com/sds/overcommit) is configured (but not
enabled if you don't use it personally) to run Rubocop and RSpec before
every commit.

Manuel Tancoigne's avatar
Manuel Tancoigne committed
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
### Rubocop - [site](https://rubocop.org/)

Rubocop checks for coding standards, allowing us to have consistent
coding style in the project. Configuration is in
[.rubocop.yml](.rubocop.yml).

Enabled plugins:

- [rubocop-performance](https://docs.rubocop.org/projects/performance),
- [rubocop-rails](https://docs.rubocop.org/projects/rails/) for common
  errors in Rails projects
- [rubocop-rspec](https://github.com/rubocop-hq/rubocop-rspec)

Run it with:

```sh
bundle exec rubocop
# To fix automatically what can be fixed:
bundle exec rubocop -a
```

### RSpec - [site](https://github.com/rspec/rspec)

RSpec examples are in `spec/`. Run the suite with:

```sh
bundle exec rspec
```

To debug step by step:
```sh
# Run this once
bundle exec rspec
# Then run this to replay failed examples
bundle exec rspec --only-failures
```

#### Acceptance tests

JSON responses are tested with
[rspec-rails-api](https://gitlab.com/experimentslabs/rspec-rails-api).

520
521
522
523
524
525
526
527
528
529
This gem is configured to generate swagger documentation in
`public/swagger.json`.

As values are changing on every RSpec runs, the following tasks were
created in order to handle the generated file:

- `rake swagger:normalize` sorts all keys in file, so diffs are easier
  to compare
- `rake swagger:changed` tells you if you need to commit or checkout the
  newly generated file (it removes all examples and compares what remains
530
  with `HEAD`). Run `normalize` before, as array order matters.
Manuel Tancoigne's avatar
Manuel Tancoigne committed
531
532
533
534
535
536
537
538
539

#### Shared contexts

As the project uses Devise for authentication, some shared contexts are
available to use in the specs:

- 'with authenticated user'
- 'with authenticated admin'

540
541
542
543
544
545
546
547
### Cucumber - [site](https://github.com/cucumber/cucumber-rails)

Cucumber is configured with
[capybara-screenshot](http://github.com/mattheworiordan/capybara-screenshot),
which makes HTML and png screenshots of pages when a step fails. Both HTML
and images screenshots are saved in `tmp/capybara_screenshots`.

By default, Cucumber will use Firefox to run the tests, but this can be
548
changed with the `BROWSER` environment variable:
549
550
551
552
553

```sh
# Default with firefox
bundle exec cucumber
# Variants
554
BROWSER=firefox-headless bundle exec cucumber
555
556
557
558
559
```

When using Chrome/Chromium, Cucumber steps will fail on Javascript
errors.

560
561
562
563
564
565
566
567
**NOTE:** We use
[Firefox ESR](https://www.mozilla.org/en-US/firefox/enterprise/) in CI.
If you want to use it locally and if Firefox ESR is not your main
Firefox version,
[download it](https://www.mozilla.org/en-US/firefox/all/#product-desktop-esr)
somewhere and set its path in `config/garden_party_instance.yml` (key:
`tests.firefox_path`).

568
569
570
571
The project uses the
[webdrivers](https://github.com/titusfortner/webdrivers) gem, which
manage the browsers respective drivers.

Manuel Tancoigne's avatar
Manuel Tancoigne committed
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
### Code coverage

When using RSpec or Cucumber, code coverage summary is generated in
`coverage/`. Don't hesitate to open it and check by yourself.

### Brakeman - [site](https://brakemanscanner.org/)

Brakeman is a "security scanner" for common mistakes leading to security
issues.

It can be launched with:

```sh
bundle exec brakeman
```
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625


## Develop another client

GardenParty is mainly an API which validates, store data and give it back
(we're kind...). The web frontend has all the logic to display the data on
a map (with OpenLayers) and only has the logic related to the
_graphical interactions_ with the map, and a bit of logic to prevent user
to submit incorrect data (which will be rejected by the server).

You're welcome to develop your own client if you want to, event if it's to
feed the API with incorrect positions, sizes etc...

**If it's the case, let us know so we can add a flag on those maps to
differentiate them from ours, and also list your awesome client in our
docs.**

Anyway, have fun.

### Notes on planning-related dates

There is something to be aware of when using the API about
planning-related dates:

- Planning dates (`implantation_planned_for`, `removal_planned_for`,
  `*_planned_for` in general) represents the day to perform a task.
- Dates are stored as UTC `datetime`s

So, provided dates for these fields _MUST_ be the beginning of the day
for the action. Which means, an
[ISO8601](https://en.wikipedia.org/wiki/ISO_8601#Combined_date_and_time_representations)
date. I.e.: with a client in UTC+1, for the beginning of 2020/12/09:

- '2021-01-08T23:00:00.000Z' is valid
- '2021-01-09T00:00:00.000+01:00' is valid too
- '2021-01-09T00:00:00.000Z' is _not_

But the backend will accept all of these, not being aware of the actual
client's timezone.