cht-conf 3.15.3 → 3.16.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +14 -0
- package/Dockerfile +23 -17
- package/README.md +62 -9
- package/devcontainer.cht-ide/.devcontainer.json +6 -0
- package/devcontainer.cht-ide/README.md +3 -0
- package/package.json +1 -1
- package/src/fn/convert-training-forms.js +17 -0
- package/src/fn/upload-training-forms.js +17 -0
- package/src/fn/validate-training-forms.js +12 -0
- package/src/fn/watch-project.js +73 -66
- package/src/lib/get-api-url.js +6 -8
- package/src/lib/main.js +63 -55
- package/src/lib/project-paths.js +1 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
## [3.16.1](https://github.com/medic/cht-conf/compare/v3.16.0...v3.16.1) (2023-03-18)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* **#524:** Don't display usage() information for commands which run successfully ([5b265e7](https://github.com/medic/cht-conf/commit/5b265e753d035b326ff8d6bac0a9aabc923048fb)), closes [#524](https://github.com/medic/cht-conf/issues/524) [#524](https://github.com/medic/cht-conf/issues/524)
|
|
7
|
+
|
|
8
|
+
# [3.16.0](https://github.com/medic/cht-conf/compare/v3.15.3...v3.16.0) (2023-03-09)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Features
|
|
12
|
+
|
|
13
|
+
* **medic/cht-core#6598:** Command line to convert and upload training forms ([#529](https://github.com/medic/cht-conf/issues/529)) ([fb700ef](https://github.com/medic/cht-conf/commit/fb700ef94fc8a0b86bb9bdeab36f42bc93970d20)), closes [medic/cht-core#6598](https://github.com/medic/cht-core/issues/6598)
|
|
14
|
+
|
|
1
15
|
## [3.15.3](https://github.com/medic/cht-conf/compare/v3.15.2...v3.15.3) (2023-02-28)
|
|
2
16
|
|
|
3
17
|
|
package/Dockerfile
CHANGED
|
@@ -1,23 +1,29 @@
|
|
|
1
|
-
FROM
|
|
2
|
-
MAINTAINER DevOps "devops@medicmobile.org"
|
|
1
|
+
FROM node:16-slim
|
|
3
2
|
|
|
3
|
+
RUN apt update \
|
|
4
|
+
&& apt install --no-install-recommends -y \
|
|
5
|
+
build-essential \
|
|
6
|
+
chromium \
|
|
7
|
+
curl \
|
|
8
|
+
git \
|
|
9
|
+
openssh-client \
|
|
10
|
+
python3-pip \
|
|
11
|
+
python3-setuptools \
|
|
12
|
+
python3-wheel \
|
|
13
|
+
xsltproc \
|
|
14
|
+
# Remove chromium to save space. We only installed it to get the transitive dependencies that are needed
|
|
15
|
+
# when running tests with puppeteer. (puppeteer-chromium-resolver will always download its own version of chromium)
|
|
16
|
+
&& apt remove -y chromium \
|
|
17
|
+
&& rm -rf /var/lib/apt/lists/*
|
|
4
18
|
|
|
5
|
-
RUN
|
|
6
|
-
apt-get update -y && \
|
|
7
|
-
apt-get install --no-install-recommends -y -q \
|
|
8
|
-
build-essential python-setuptools \
|
|
9
|
-
python python-pip python-dev \
|
|
10
|
-
libffi-dev libssl-dev \
|
|
11
|
-
libxml2-dev libxslt1-dev zlib1g-dev \
|
|
12
|
-
git wget python-wheel curl
|
|
19
|
+
RUN python3 -m pip install git+https://github.com/medic/pyxform.git@medic-conf-1.17#egg=pyxform-medic
|
|
13
20
|
|
|
14
|
-
RUN
|
|
15
|
-
python -m pip install git+https://github.com/medic/pyxform.git@medic-conf-1.17#egg=pyxform-medic
|
|
21
|
+
RUN npm install -g cht-conf
|
|
16
22
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
23
|
+
# Using the 1000:1000 user is recommended for VSCode dev containers
|
|
24
|
+
# https://code.visualstudio.com/remote/advancedcontainers/add-nonroot-user
|
|
25
|
+
USER node
|
|
20
26
|
|
|
21
|
-
|
|
27
|
+
WORKDIR /workdir
|
|
22
28
|
|
|
23
|
-
|
|
29
|
+
ENTRYPOINT ["cht"]
|
package/README.md
CHANGED
|
@@ -32,22 +32,75 @@ As Administrator:
|
|
|
32
32
|
|
|
33
33
|
### Docker
|
|
34
34
|
|
|
35
|
-
|
|
35
|
+
CHT Conf can also be run from within a Docker container. This is useful if you are already familiar with Docker and do not wish to configure the various dependencies required for developing CHT apps on your local machine. The necessary dependencies are pre-packaged in the Docker image.
|
|
36
36
|
|
|
37
|
-
|
|
37
|
+
#### Building the image
|
|
38
38
|
|
|
39
|
-
|
|
40
|
-
docker run -d --name cht-conf cht-conf:v0
|
|
39
|
+
Install [Docker](https://www.docker.com/). If you are using Windows, you also need to enable the [Windows Subsystem for Linux (WSL2)](https://learn.microsoft.com/en-us/windows/wsl/install) to perform the following steps.
|
|
41
40
|
|
|
42
|
-
|
|
41
|
+
Using the terminal (or the WLS shell on Windows: _Start > wsl_), run the following commands to create a new project directory and build the Docker image:
|
|
43
42
|
|
|
44
|
-
|
|
43
|
+
```shell
|
|
44
|
+
mkdir -p ~/cht-project
|
|
45
|
+
cd ~/cht-project
|
|
46
|
+
curl https://raw.githubusercontent.com/medic/cht-conf/main/Dockerfile > Dockerfile
|
|
47
|
+
docker build -t cht-ide .
|
|
48
|
+
```
|
|
45
49
|
|
|
46
|
-
|
|
50
|
+
#### Using the image
|
|
47
51
|
|
|
48
|
-
|
|
52
|
+
The resulting Docker image can be used as a [VSCode Development Container](https://code.visualstudio.com/docs/devcontainers/containers) (easiest) or as a standalone Docker utility.
|
|
49
53
|
|
|
50
|
-
|
|
54
|
+
##### VSCode Development Container
|
|
55
|
+
|
|
56
|
+
If you want to develop CHT apps with VSCode, you can use the Docker image as a Development Container. This will allow you to use the `cht-conf` utility and its associated tech stack from within VSCode (without needing to install dependencies like NodeJS on your host system).
|
|
57
|
+
|
|
58
|
+
[Install VSCode](https://code.visualstudio.com/) if you do not have it already.
|
|
59
|
+
|
|
60
|
+
Using the terminal (or the WLS shell on Windows: _Start > wsl_), run the following commands from within your project directory (created above) to download the `.devcontainer.json` config file, install the [Dev Containers extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers), and open the project directory in VSCode:
|
|
61
|
+
|
|
62
|
+
```shell
|
|
63
|
+
cd ~/cht-project
|
|
64
|
+
curl -s https://raw.githubusercontent.com/medic/cht-conf/main/devcontainer.cht-ide/.devcontainer.json > .devcontainer.json
|
|
65
|
+
code --install-extension ms-vscode-remote.remote-containers
|
|
66
|
+
code -n .
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
When opening VSCode, you may be prompted with the question:
|
|
70
|
+
|
|
71
|
+
> **Do you trust the authors of the files in this folder?**
|
|
72
|
+
|
|
73
|
+
Choose, "Yes, I trust the authors".
|
|
74
|
+
|
|
75
|
+
Open the Command Palette in VSCode (_Ctrl+Shift+P_ or _Cmd+Shift+P_) and select `Reopen in Container`. This will open your workspace inside a container based on the `cht-ide` image. You can use the `cht` commands by opening a terminal in VSCode (_Ctrl+Shift+\`_ or _Cmd+Shift+\`_). If prompted "Do you trust the authors..." choose "Trust Folder & Continue".
|
|
76
|
+
|
|
77
|
+
Run the following command in the VSCode terminal to bootstrap your new CHT project:
|
|
78
|
+
|
|
79
|
+
```shell
|
|
80
|
+
cht initialise-project-layout
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
###### Terminal environment
|
|
84
|
+
|
|
85
|
+
When opening a terminal in VSCode in a development container, the terminal will be running on the _container environment_ by default. This is what gives you access to the various `cht` commands. However, this also means you do NOT have access, within the default VSCode terminal, to commands from your _host environment_. So, for example, you cannot run `docker` commands since Docker is not installed inside the container.
|
|
86
|
+
|
|
87
|
+
To open a terminal running on you _host environment_ in VSCode, open the Command Palette (_Ctrl+Shift+P_ or _Cmd+Shift+P_) and select `Create New Integrated Terminal (Local)`. Just remember that you will NOT be able to run `cht` commands from this terminal since cht-conf is not installed on your host machine.
|
|
88
|
+
|
|
89
|
+
##### Standalone Docker utility
|
|
90
|
+
|
|
91
|
+
If you are not using VSCode, you can use the Docker image as a standalone utility from the command line. Instead of using the `cht ...` command, you can run `docker run -it --rm -v "$PWD":/workdir cht-ide ...`. This will create an ephemeral container with access to your current directory that will run the given cht command. (Do not include the `cht` part of the command, just your desired actions/parameters.)
|
|
92
|
+
|
|
93
|
+
Run the following command inside the project directory to bootstrap your new CHT project:
|
|
94
|
+
|
|
95
|
+
```shell
|
|
96
|
+
docker run -it --rm -v "$PWD":/workdir cht-ide initialise-project-layout
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
#### Note on connecting to a local CHT instance
|
|
100
|
+
|
|
101
|
+
When using `cht-conf` within a Docker container to connect to a CHT instance that is running on your local machine (e.g. a development instance), you cannot use the `--local` flag or `localhost` in your `--url` parameter (since these will be interpreted as "local to the container").
|
|
102
|
+
|
|
103
|
+
It is recommended to run a local CHT instance using the [CHT Docker Helper script](https://docs.communityhealthtoolkit.org/apps/guides/hosting/4.x/app-developer/). You can connect to the resulting `...my.local-ip.co` URL from the Docker container (or the VSCode terminal). (Just make sure the port your CHT instance is hosted on is not blocked by your firewall).
|
|
51
104
|
|
|
52
105
|
## Bash completion
|
|
53
106
|
|
package/package.json
CHANGED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
const convertForms = require('../lib/convert-forms').execute;
|
|
2
|
+
const environment = require('../lib/environment');
|
|
3
|
+
const { TRAINING_FORMS_PATH } = require('../lib/project-paths');
|
|
4
|
+
|
|
5
|
+
const convertTrainingForms = (forms) => {
|
|
6
|
+
return convertForms(environment.pathToProject, 'training', {
|
|
7
|
+
enketo: true,
|
|
8
|
+
forms: forms,
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
module.exports = {
|
|
13
|
+
requiresInstance: false,
|
|
14
|
+
convertTrainingForms,
|
|
15
|
+
TRAINING_FORMS_PATH,
|
|
16
|
+
execute: () => convertTrainingForms(environment.extraArgs)
|
|
17
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
const environment = require('../lib/environment');
|
|
2
|
+
const uploadForms = require('../lib/upload-forms').execute;
|
|
3
|
+
const { TRAINING_FORMS_PATH } = require('../lib/project-paths');
|
|
4
|
+
|
|
5
|
+
const uploadTrainingForms = (forms) => {
|
|
6
|
+
return uploadForms(environment.pathToProject, 'training', {
|
|
7
|
+
id_prefix: 'training:',
|
|
8
|
+
forms: forms,
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
module.exports = {
|
|
13
|
+
requiresInstance: true,
|
|
14
|
+
uploadTrainingForms,
|
|
15
|
+
TRAINING_FORMS_PATH,
|
|
16
|
+
execute: () => uploadTrainingForms(environment.extraArgs)
|
|
17
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
const validateForms = require('../lib/validate-forms');
|
|
2
|
+
const environment = require('../lib/environment');
|
|
3
|
+
|
|
4
|
+
const validateTrainingForms = (forms) => {
|
|
5
|
+
return validateForms(environment.pathToProject, 'training', { forms });
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
module.exports = {
|
|
9
|
+
requiresInstance: false,
|
|
10
|
+
validateTrainingForms,
|
|
11
|
+
execute: () => validateTrainingForms(environment.extraArgs)
|
|
12
|
+
};
|
package/src/fn/watch-project.js
CHANGED
|
@@ -8,14 +8,17 @@ const watcher = require('@parcel/watcher');
|
|
|
8
8
|
const { validateAppForms } = require('./validate-app-forms');
|
|
9
9
|
const { validateContactForms } = require('./validate-contact-forms');
|
|
10
10
|
const { validateCollectForms } = require('./validate-collect-forms');
|
|
11
|
+
const { validateTrainingForms } = require('./validate-training-forms');
|
|
11
12
|
const { uploadAppForms } = require('./upload-app-forms');
|
|
12
13
|
const { uploadContactForms } = require('./upload-contact-forms');
|
|
13
14
|
const { uploadCollectForms } = require('./upload-collect-forms');
|
|
15
|
+
const { uploadTrainingForms } = require('./upload-training-forms');
|
|
14
16
|
const convertForms = require('../lib/convert-forms');
|
|
15
17
|
const uploadForms = require('../lib/upload-forms');
|
|
16
18
|
const { deleteForms } = require('../fn/delete-forms');
|
|
17
19
|
const { convertAppForms, APP_FORMS_PATH } = require('./convert-app-forms');
|
|
18
20
|
const { convertContactForm, CONTACT_FORMS_PATH } = require('./convert-contact-forms');
|
|
21
|
+
const { convertTrainingForms, TRAINING_FORMS_PATH } = require('./convert-training-forms');
|
|
19
22
|
const { convertCollectForms, COLLECT_FORMS_PATH } = require('./convert-collect-forms');
|
|
20
23
|
const { uploadAppSettings, APP_SETTINGS_DIR_PATH, APP_SETTINGS_JSON_PATH } = require('./upload-app-settings');
|
|
21
24
|
const { execute: compileAppSettings, configFileMatcher } = require('./compile-app-settings');
|
|
@@ -39,8 +42,10 @@ const uploadInitialState = async (api) => {
|
|
|
39
42
|
await uploadResources();
|
|
40
43
|
await runValidation(validateAppForms, environment.extraArgs);
|
|
41
44
|
await runValidation(validateContactForms, environment.extraArgs);
|
|
45
|
+
await runValidation(validateTrainingForms, environment.extraArgs);
|
|
42
46
|
await uploadAppForms(environment.extraArgs);
|
|
43
47
|
await uploadContactForms(environment.extraArgs);
|
|
48
|
+
await uploadTrainingForms(environment.extraArgs);
|
|
44
49
|
await uploadCustomTranslations();
|
|
45
50
|
await uploadAppSettings(api);
|
|
46
51
|
};
|
|
@@ -87,62 +92,64 @@ const deleteForm = (fileName, formDir) => {
|
|
|
87
92
|
return false;
|
|
88
93
|
};
|
|
89
94
|
|
|
90
|
-
const
|
|
91
|
-
if (
|
|
92
|
-
return
|
|
95
|
+
const getFormCommandContext = (parsedPath) => {
|
|
96
|
+
if (parsedPath.dir === path.join(environment.pathToProject, APP_FORMS_PATH)) {
|
|
97
|
+
return {
|
|
98
|
+
path: APP_FORMS_PATH,
|
|
99
|
+
validateForms: validateAppForms,
|
|
100
|
+
uploadForms: uploadAppForms,
|
|
101
|
+
convertForms: convertAppForms,
|
|
102
|
+
};
|
|
93
103
|
}
|
|
94
|
-
|
|
95
|
-
if (
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
104
|
+
|
|
105
|
+
if (parsedPath.dir === path.join(environment.pathToProject, TRAINING_FORMS_PATH)) {
|
|
106
|
+
return {
|
|
107
|
+
path: TRAINING_FORMS_PATH,
|
|
108
|
+
validateForms: validateTrainingForms,
|
|
109
|
+
uploadForms: uploadTrainingForms,
|
|
110
|
+
convertForms: convertTrainingForms,
|
|
111
|
+
};
|
|
102
112
|
}
|
|
103
113
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
114
|
+
if (parsedPath.dir === path.join(environment.pathToProject, CONTACT_FORMS_PATH)) {
|
|
115
|
+
return {
|
|
116
|
+
path: CONTACT_FORMS_PATH,
|
|
117
|
+
validateForms: validateContactForms,
|
|
118
|
+
uploadForms: uploadContactForms,
|
|
119
|
+
convertForms: convertContactForm,
|
|
120
|
+
};
|
|
111
121
|
}
|
|
112
|
-
return false;
|
|
113
|
-
};
|
|
114
122
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
});
|
|
123
|
-
return true;
|
|
123
|
+
if (parsedPath.dir === path.join(environment.pathToProject, COLLECT_FORMS_PATH)) {
|
|
124
|
+
return {
|
|
125
|
+
path: COLLECT_FORMS_PATH,
|
|
126
|
+
validateForms: validateCollectForms,
|
|
127
|
+
uploadForms: uploadCollectForms,
|
|
128
|
+
convertForms: convertCollectForms,
|
|
129
|
+
};
|
|
124
130
|
}
|
|
125
|
-
|
|
131
|
+
|
|
132
|
+
return;
|
|
126
133
|
};
|
|
127
134
|
|
|
128
|
-
const
|
|
129
|
-
if (eventType === watcherEvents.DeleteEvent && deleteForm(fileName,
|
|
135
|
+
const processForm = (eventType, fileName, commandContext) => {
|
|
136
|
+
if (eventType === watcherEvents.DeleteEvent && deleteForm(fileName, commandContext.path)) {
|
|
130
137
|
return true;
|
|
131
138
|
}
|
|
132
|
-
let form =
|
|
139
|
+
let form = uploadForms.formFileMatcher(fileName);
|
|
133
140
|
if (form) {
|
|
134
141
|
eventQueue.enqueue(async () => {
|
|
135
|
-
await
|
|
142
|
+
await runValidation(commandContext.validateForms, [form]);
|
|
143
|
+
await commandContext.uploadForms([form]);
|
|
136
144
|
return fileName;
|
|
137
145
|
});
|
|
138
146
|
return true;
|
|
139
147
|
}
|
|
140
148
|
|
|
141
|
-
form =
|
|
149
|
+
form = convertForms.formFileMatcher(fileName);
|
|
142
150
|
if (form) {
|
|
143
151
|
eventQueue.enqueue(async () => {
|
|
144
|
-
await
|
|
145
|
-
await uploadContactForms([form]);
|
|
152
|
+
await commandContext.convertForms([form]);
|
|
146
153
|
return fileName;
|
|
147
154
|
});
|
|
148
155
|
return true;
|
|
@@ -150,24 +157,34 @@ const processContactForm = (eventType, fileName) => {
|
|
|
150
157
|
return false;
|
|
151
158
|
};
|
|
152
159
|
|
|
153
|
-
const
|
|
154
|
-
if (
|
|
155
|
-
|
|
160
|
+
const getFormMediaCommandContext = (parsedPath) => {
|
|
161
|
+
if (parsedPath.dir.startsWith(path.join(environment.pathToProject, APP_FORMS_PATH))
|
|
162
|
+
&& path.parse(parsedPath.dir).dir.endsWith(APP_FORMS_PATH)) {
|
|
163
|
+
// Check if the directory's immediate parent is forms/app
|
|
164
|
+
return {
|
|
165
|
+
validateForms: validateAppForms,
|
|
166
|
+
uploadForms: uploadAppForms,
|
|
167
|
+
};
|
|
156
168
|
}
|
|
157
|
-
|
|
158
|
-
if (
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
169
|
+
|
|
170
|
+
if (parsedPath.dir.startsWith(path.join(environment.pathToProject, TRAINING_FORMS_PATH))
|
|
171
|
+
&& path.parse(parsedPath.dir).dir.endsWith(TRAINING_FORMS_PATH)) {
|
|
172
|
+
// Check if the directory's immediate parent is forms/training
|
|
173
|
+
return {
|
|
174
|
+
validateForms: validateTrainingForms,
|
|
175
|
+
uploadForms: uploadTrainingForms,
|
|
176
|
+
};
|
|
165
177
|
}
|
|
166
178
|
|
|
167
|
-
|
|
179
|
+
return;
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
const processFormMedia = (formMediaDir, fileName, commandContext) => {
|
|
183
|
+
const form = uploadForms.formMediaMatcher(formMediaDir);
|
|
168
184
|
if (form) {
|
|
169
185
|
eventQueue.enqueue(async () => {
|
|
170
|
-
await
|
|
186
|
+
await runValidation(commandContext.validateForms,[form]);
|
|
187
|
+
await commandContext.uploadForms([form]);
|
|
171
188
|
return fileName;
|
|
172
189
|
});
|
|
173
190
|
return true;
|
|
@@ -212,29 +229,19 @@ const watchProject = {
|
|
|
212
229
|
const parsedPath = path.parse(changePath);
|
|
213
230
|
const fileName = parsedPath.base;
|
|
214
231
|
|
|
215
|
-
|
|
216
|
-
|
|
232
|
+
const formContext = getFormCommandContext(parsedPath);
|
|
233
|
+
if (formContext) {
|
|
234
|
+
processForm(event.type, fileName, formContext);
|
|
217
235
|
continue;
|
|
218
236
|
}
|
|
219
237
|
|
|
220
|
-
|
|
221
|
-
|
|
238
|
+
const formMediaContext = getFormMediaCommandContext(parsedPath);
|
|
239
|
+
if (formMediaContext) {
|
|
222
240
|
const dirName = path.parse(parsedPath.dir).base;
|
|
223
|
-
|
|
241
|
+
processFormMedia(dirName, fileName, formMediaContext);
|
|
224
242
|
continue;
|
|
225
243
|
}
|
|
226
244
|
|
|
227
|
-
if (parsedPath.dir === path.join(environment.pathToProject, CONTACT_FORMS_PATH)) {
|
|
228
|
-
processContactForm(event.type, fileName);
|
|
229
|
-
continue;
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
if (parsedPath.dir === path.join(environment.pathToProject, COLLECT_FORMS_PATH)) {
|
|
233
|
-
processCollectForm(event.type, fileName);
|
|
234
|
-
continue;
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
|
|
238
245
|
if (parsedPath.dir === path.join(environment.pathToProject, TRANSLATIONS_DIR_PATH)) {
|
|
239
246
|
eventQueue.enqueue(async () => {
|
|
240
247
|
await uploadCustomTranslations();
|
package/src/lib/get-api-url.js
CHANGED
|
@@ -1,21 +1,20 @@
|
|
|
1
1
|
const userPrompt = require('./user-prompt');
|
|
2
2
|
const url = require('url');
|
|
3
|
-
const usage = require('../cli/usage');
|
|
4
3
|
|
|
5
4
|
const emoji = require('./emoji');
|
|
6
|
-
const {
|
|
5
|
+
const { info } = require('./log');
|
|
6
|
+
const usage = require('../cli/usage');
|
|
7
7
|
|
|
8
8
|
const getApiUrl = (cmdArgs, env = {}) => {
|
|
9
9
|
const specifiedModes = [cmdArgs.local, cmdArgs.instance, cmdArgs.url, cmdArgs.archive].filter(mode => mode);
|
|
10
10
|
if (specifiedModes.length !== 1) {
|
|
11
|
-
error('Require exactly one of these parameter: --local --instance --url --archive');
|
|
12
11
|
usage();
|
|
13
|
-
|
|
12
|
+
throw Error('One of these parameters is required: --local --instance --url --archive');
|
|
14
13
|
}
|
|
15
14
|
|
|
16
15
|
if (cmdArgs.user && !cmdArgs.instance) {
|
|
17
|
-
|
|
18
|
-
|
|
16
|
+
usage();
|
|
17
|
+
throw Error('The --user switch must be accompanied with --instance');
|
|
19
18
|
}
|
|
20
19
|
|
|
21
20
|
let instanceUrl;
|
|
@@ -26,8 +25,7 @@ const getApiUrl = (cmdArgs, env = {}) => {
|
|
|
26
25
|
// See ./archiving-db.js
|
|
27
26
|
instanceUrl = parseLocalUrl(env.COUCH_URL);
|
|
28
27
|
if (instanceUrl.hostname !== 'localhost') {
|
|
29
|
-
|
|
30
|
-
return false;
|
|
28
|
+
throw Error(`--local was specified but COUCH_URL env var is set to '${instanceUrl.hostname}'. Please use --url for remote servers.`);
|
|
31
29
|
}
|
|
32
30
|
} else if (cmdArgs.instance) {
|
|
33
31
|
const password = userPrompt.question(`${emoji.key} Password: `, { hideEchoBack: true });
|
package/src/lib/main.js
CHANGED
|
@@ -21,14 +21,17 @@ const defaultActions = [
|
|
|
21
21
|
'convert-app-forms',
|
|
22
22
|
'convert-collect-forms',
|
|
23
23
|
'convert-contact-forms',
|
|
24
|
+
'convert-training-forms',
|
|
24
25
|
'validate-app-forms',
|
|
25
26
|
'validate-collect-forms',
|
|
26
27
|
'validate-contact-forms',
|
|
28
|
+
'validate-training-forms',
|
|
27
29
|
'backup-all-forms',
|
|
28
30
|
'delete-all-forms',
|
|
29
31
|
'upload-app-forms',
|
|
30
32
|
'upload-collect-forms',
|
|
31
33
|
'upload-contact-forms',
|
|
34
|
+
'upload-training-forms',
|
|
32
35
|
'upload-resources',
|
|
33
36
|
'upload-branding',
|
|
34
37
|
'upload-partners',
|
|
@@ -41,12 +44,15 @@ const defaultArchiveActions = [
|
|
|
41
44
|
'convert-app-forms',
|
|
42
45
|
'convert-collect-forms',
|
|
43
46
|
'convert-contact-forms',
|
|
47
|
+
'convert-training-forms',
|
|
44
48
|
'validate-app-forms',
|
|
45
49
|
'validate-collect-forms',
|
|
46
50
|
'validate-contact-forms',
|
|
51
|
+
'validate-training-forms',
|
|
47
52
|
'upload-app-forms',
|
|
48
53
|
'upload-collect-forms',
|
|
49
54
|
'upload-contact-forms',
|
|
55
|
+
'upload-training-forms',
|
|
50
56
|
'upload-resources',
|
|
51
57
|
'upload-branding',
|
|
52
58
|
'upload-partners',
|
|
@@ -128,69 +134,16 @@ module.exports = async (argv, env) => {
|
|
|
128
134
|
//
|
|
129
135
|
// Build up actions
|
|
130
136
|
//
|
|
131
|
-
let actions = cmdArgs._;
|
|
132
|
-
if (actions.length) {
|
|
133
|
-
const unsupported = actions.filter(a => !supportedActions.includes(a));
|
|
134
|
-
if(unsupported.length) {
|
|
135
|
-
throw new Error(`Unsupported action(s): ${unsupported.join(' ')}`);
|
|
136
|
-
}
|
|
137
|
-
} else {
|
|
138
|
-
actions = !cmdArgs.archive ? defaultActions : defaultArchiveActions;
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
if (cmdArgs['skip-git-check']) {
|
|
142
|
-
actions = actions.filter(a => a !== 'check-git');
|
|
143
|
-
}
|
|
144
|
-
|
|
145
137
|
const skipValidate = cmdArgs['skip-validate'];
|
|
146
|
-
|
|
147
|
-
warn('Skipping all form validation.');
|
|
148
|
-
const validateActions = [
|
|
149
|
-
'validate-app-forms',
|
|
150
|
-
'validate-collect-forms',
|
|
151
|
-
'validate-contact-forms'
|
|
152
|
-
];
|
|
153
|
-
actions = actions.filter(action => !validateActions.includes(action));
|
|
154
|
-
} else {
|
|
155
|
-
const addFormValidationIfNecessary = (formType) => {
|
|
156
|
-
const updateFormsIndex = actions.indexOf(`upload-${formType}-forms`);
|
|
157
|
-
if (updateFormsIndex >= 0 && actions.indexOf(`validate-${formType}-forms`) < 0) {
|
|
158
|
-
actions.splice(updateFormsIndex, 0, `validate-${formType}-forms`);
|
|
159
|
-
}
|
|
160
|
-
};
|
|
161
|
-
addFormValidationIfNecessary('app');
|
|
162
|
-
addFormValidationIfNecessary('collect');
|
|
163
|
-
addFormValidationIfNecessary('contact');
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
actions = actions.map(actionName => {
|
|
167
|
-
const action = require(`../fn/${actionName}`);
|
|
168
|
-
|
|
169
|
-
if (typeof action.execute !== 'function') {
|
|
170
|
-
throw new Error(`${actionName} has not been implemented correctly: no 'execute' function`);
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
if (!Object.hasOwnProperty.call(action, 'requiresInstance')) {
|
|
174
|
-
action.requiresInstance = true;
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
action.name = actionName;
|
|
178
|
-
|
|
179
|
-
return action;
|
|
180
|
-
});
|
|
138
|
+
const actions = buildActions(cmdArgs, skipValidate);
|
|
181
139
|
|
|
182
140
|
//
|
|
183
141
|
// Initialize the environment
|
|
184
142
|
//
|
|
185
143
|
const projectName = fs.path.basename(pathToProject);
|
|
186
144
|
|
|
187
|
-
const apiUrl = getApiUrl(cmdArgs, env);
|
|
188
145
|
const requiresInstance = actions.some(action => action.requiresInstance);
|
|
189
|
-
|
|
190
|
-
if (!apiUrl) {
|
|
191
|
-
throw new Error('Failed to obtain a url to the API');
|
|
192
|
-
}
|
|
193
|
-
}
|
|
146
|
+
const apiUrl = requiresInstance && getApiUrl(cmdArgs, env);
|
|
194
147
|
|
|
195
148
|
let extraArgs = cmdArgs['--'];
|
|
196
149
|
if (!extraArgs.length) {
|
|
@@ -244,5 +197,60 @@ module.exports = async (argv, env) => {
|
|
|
244
197
|
}
|
|
245
198
|
};
|
|
246
199
|
|
|
200
|
+
function buildActions(cmdArgs, skipValidate) {
|
|
201
|
+
let actions = cmdArgs._;
|
|
202
|
+
if (actions.length) {
|
|
203
|
+
const unsupported = actions.filter(a => !supportedActions.includes(a));
|
|
204
|
+
if (unsupported.length) {
|
|
205
|
+
usage();
|
|
206
|
+
throw new Error(`Unsupported action(s): ${unsupported.join(' ')}`);
|
|
207
|
+
}
|
|
208
|
+
} else {
|
|
209
|
+
actions = !cmdArgs.archive ? defaultActions : defaultArchiveActions;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
if (cmdArgs['skip-git-check']) {
|
|
213
|
+
actions = actions.filter(a => a !== 'check-git');
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
if(skipValidate) {
|
|
217
|
+
warn('Skipping all form validation.');
|
|
218
|
+
const validateActions = [
|
|
219
|
+
'validate-app-forms',
|
|
220
|
+
'validate-collect-forms',
|
|
221
|
+
'validate-contact-forms',
|
|
222
|
+
'validate-training-forms',
|
|
223
|
+
];
|
|
224
|
+
actions = actions.filter(action => !validateActions.includes(action));
|
|
225
|
+
} else {
|
|
226
|
+
const addFormValidationIfNecessary = (formType) => {
|
|
227
|
+
const updateFormsIndex = actions.indexOf(`upload-${formType}-forms`);
|
|
228
|
+
if (updateFormsIndex >= 0 && actions.indexOf(`validate-${formType}-forms`) < 0) {
|
|
229
|
+
actions.splice(updateFormsIndex, 0, `validate-${formType}-forms`);
|
|
230
|
+
}
|
|
231
|
+
};
|
|
232
|
+
addFormValidationIfNecessary('app');
|
|
233
|
+
addFormValidationIfNecessary('collect');
|
|
234
|
+
addFormValidationIfNecessary('contact');
|
|
235
|
+
addFormValidationIfNecessary('training');
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
return actions.map(actionName => {
|
|
239
|
+
const action = require(`../fn/${actionName}`);
|
|
240
|
+
|
|
241
|
+
if (typeof action.execute !== 'function') {
|
|
242
|
+
throw new Error(`${actionName} has not been implemented correctly: no 'execute' function`);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
if (!Object.hasOwnProperty.call(action, 'requiresInstance')) {
|
|
246
|
+
action.requiresInstance = true;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
action.name = actionName;
|
|
250
|
+
|
|
251
|
+
return action;
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
|
|
247
255
|
// Exists for generic mocking purposes
|
|
248
256
|
const executeAction = action => action.execute();
|
package/src/lib/project-paths.js
CHANGED
|
@@ -6,6 +6,7 @@ module.exports = {
|
|
|
6
6
|
APP_FORMS_PATH: path.join('forms', 'app'),
|
|
7
7
|
CONTACT_FORMS_PATH: path.join('forms', 'contact'),
|
|
8
8
|
COLLECT_FORMS_PATH: path.join('forms', 'collect'),
|
|
9
|
+
TRAINING_FORMS_PATH: path.join('forms', 'training'),
|
|
9
10
|
TRANSLATIONS_DIR_PATH: 'translations',
|
|
10
11
|
RESOURCE_CONFIG_PATH: 'resources.json',
|
|
11
12
|
RESOURCES_DIR_PATH: 'resources'
|