antelope-cli 1.1.7 → 1.2.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/ansible/onboarding-ca/README.md +72 -0
- package/ansible/onboarding-ca/onboard-ca.yml +41 -0
- package/ansible/onboarding-ca/roles/onboard_ca/defaults/main.yml +24 -0
- package/ansible/onboarding-ca/roles/onboard_ca/meta/main.yml +10 -0
- package/ansible/onboarding-ca/roles/onboard_ca/tasks/main.yml +76 -0
- package/ansible/onboarding-ca/roles/onboard_ca/tasks/per_branch.yml +106 -0
- package/ansible/onboarding-ca/roles/onboard_ca/vars/main.yml +6 -0
- package/index.js +4 -0
- package/onboarding-ca/brand.js +13 -0
- package/onboarding-ca/index.js +94 -0
- package/onboarding-ca/riskDisclaimer.js +27 -0
- package/onboarding-ca/scss.js +24 -0
- package/onboarding-ca/templates/risk-disclaimer.html +12 -0
- package/onboarding-crm/brand.js +21 -0
- package/onboarding-crm/createConfig.js +38 -0
- package/onboarding-crm/index.js +157 -0
- package/onboarding-crm/themes.js +92 -0
- package/onboarding-crm/translations.js +37 -0
- package/onboarding-crm/variablesColors.js +54 -0
- package/package.json +1 -1
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# Client Area brand onboarding (Ansible)
|
|
2
|
+
|
|
3
|
+
Automates onboarding a new client brand into
|
|
4
|
+
[`web-client-area`](https://bitbucket.org/xsitesinc/web-client-area): it creates the brand
|
|
5
|
+
assets and opens the pull request(s), reproducing the manual onboarding PRs
|
|
6
|
+
(e.g. [#4266](https://bitbucket.org/xsitesinc/web-client-area/pull-requests/4266)).
|
|
7
|
+
|
|
8
|
+
Per brand it creates, under the target repo:
|
|
9
|
+
|
|
10
|
+
- `src/assets/other/<folder>/<folder>.scss` — empty stylesheet
|
|
11
|
+
- `src/assets/other/<folder>/legal/risk-disclaimer.html` — the standard disclaimer
|
|
12
|
+
|
|
13
|
+
The file creation is delegated to the `onboarding-ca` command of the published
|
|
14
|
+
[`antelope-cli`](https://www.npmjs.com/package/antelope-cli) (`npx antelope-cli@<version> onboarding-ca`);
|
|
15
|
+
this role owns cloning, branching, committing, pushing and PR creation.
|
|
16
|
+
|
|
17
|
+
## Preconditions (control node)
|
|
18
|
+
|
|
19
|
+
- `git`, Node.js (engines `^16.20.2`) and `npx` on `PATH`.
|
|
20
|
+
- An **SSH key authorized to push** to `xsitesinc/web-client-area` (push is over SSH).
|
|
21
|
+
- A **Bitbucket API token** with pull-request write access (used only for the REST calls:
|
|
22
|
+
reviewer lookup + PR creation). Store it in Ansible Vault or pass via environment — never commit it.
|
|
23
|
+
- `ansible.builtin.uri` needs Python `urllib` (standard). No extra collections required.
|
|
24
|
+
|
|
25
|
+
## Usage
|
|
26
|
+
|
|
27
|
+
Interactive (prompts for the inputs):
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
ansible-playbook onboard-ca.yml
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Non-interactive (for CI / wrapping playbooks) — extra-vars skip the prompts:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
ansible-playbook onboard-ca.yml \
|
|
37
|
+
-e brand_name="Zenith Horizon Group" \
|
|
38
|
+
-e jira_key=ATLP-47067 \
|
|
39
|
+
-e dest_branches=develop,master-26.9 \
|
|
40
|
+
-e bitbucket_token="$BITBUCKET_TOKEN"
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Inputs
|
|
44
|
+
|
|
45
|
+
| var | required | example | notes |
|
|
46
|
+
|-----|----------|---------|-------|
|
|
47
|
+
| `brand_name` | yes | `Zenith Horizon Group` | display name → PR title |
|
|
48
|
+
| `jira_key` | yes | `ATLP-47067` | PR title prefix + branch name |
|
|
49
|
+
| `dest_branches` | yes | `develop,master-26.9` | comma-separated; **one PR per branch** |
|
|
50
|
+
| `bitbucket_token` | yes | *(secret)* | Bearer token for the REST calls |
|
|
51
|
+
| `brand_assets_folder` | no | `zenithhorizongroup` | folder name → CLI `--brand_assets_folder`, branch name; prompted separately. Blank ⇒ derived from `brand_name` (lowercased, spaces removed) |
|
|
52
|
+
| `reviewers` | no | *(auto)* | default: repo's Bitbucket default-reviewers (author excluded); override with `[{account_id: "..."}]` |
|
|
53
|
+
| `antelope_cli_version` | no | `1.2.0` | pinned published CLI version |
|
|
54
|
+
| `work_dir` | no | `/tmp/onboard-ca/web-client-area` | clone location |
|
|
55
|
+
| `git_user_name` / `git_user_email` | no | | commit identity |
|
|
56
|
+
|
|
57
|
+
## Behavior notes
|
|
58
|
+
|
|
59
|
+
- **One PR per destination branch.** `develop` → `feature/<jira>-onboarding-<folder>`;
|
|
60
|
+
`master-*`/`release-*` → `hotfix/<jira>-onboarding-<folder>`. Each branch is based on its own
|
|
61
|
+
destination so diverged histories don't bleed across PRs.
|
|
62
|
+
- **Reviewers** are read from the repo's Bitbucket *Default reviewers* config; the authenticating
|
|
63
|
+
account is excluded (Bitbucket rejects a PR whose author is a reviewer).
|
|
64
|
+
- **Idempotent:** re-running with the same inputs recreates the (identical) files on the existing
|
|
65
|
+
source branch, detects no staged changes, skips commit/push, and tolerates the "PR already exists"
|
|
66
|
+
response (HTTP 400).
|
|
67
|
+
- The playbook prints a summary of created PRs (destination, source branch, status, URL).
|
|
68
|
+
|
|
69
|
+
## Scope
|
|
70
|
+
|
|
71
|
+
Client Area only. web-crm onboarding is a separate, independent process (its file edits live in
|
|
72
|
+
antelope-cli's existing `onboarding` command) and is not run by this playbook.
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
---
|
|
2
|
+
# Onboard a Client Area brand into web-client-area and open the PR(s).
|
|
3
|
+
#
|
|
4
|
+
# Interactive:
|
|
5
|
+
# ansible-playbook onboard-ca.yml
|
|
6
|
+
#
|
|
7
|
+
# Non-interactive (e.g. from another playbook / CI), pass everything as extra-vars:
|
|
8
|
+
# ansible-playbook onboard-ca.yml \
|
|
9
|
+
# -e brand_name="Zenith Horizon Group" \
|
|
10
|
+
# -e brand_assets_folder=zenithhorizongroup \
|
|
11
|
+
# -e jira_key=ATLP-47067 \
|
|
12
|
+
# -e dest_branches=develop,master-26.9 \
|
|
13
|
+
# -e bitbucket_token="$BITBUCKET_TOKEN"
|
|
14
|
+
#
|
|
15
|
+
# Preconditions on the control node: git + Node (>=16) + npx, an SSH key authorized to
|
|
16
|
+
# push to xsitesinc/web-client-area, and a Bitbucket API token with PR write access.
|
|
17
|
+
- name: Onboard a Client Area brand (web-client-area)
|
|
18
|
+
hosts: localhost
|
|
19
|
+
connection: local
|
|
20
|
+
gather_facts: false
|
|
21
|
+
|
|
22
|
+
vars_prompt:
|
|
23
|
+
- name: brand_name
|
|
24
|
+
prompt: "Brand display name — used in the PR title (e.g. Zenith Horizon Group)"
|
|
25
|
+
private: false
|
|
26
|
+
- name: brand_assets_folder
|
|
27
|
+
prompt: "Brand assets folder name — the src/assets/other/<folder> (e.g. zenithhorizongroup); leave blank to derive from the display name"
|
|
28
|
+
default: ""
|
|
29
|
+
private: false
|
|
30
|
+
- name: jira_key
|
|
31
|
+
prompt: "Jira key (e.g. ATLP-47067)"
|
|
32
|
+
private: false
|
|
33
|
+
- name: dest_branches
|
|
34
|
+
prompt: "Destination branches, comma-separated (e.g. develop,master-26.9)"
|
|
35
|
+
private: false
|
|
36
|
+
- name: bitbucket_token
|
|
37
|
+
prompt: "Bitbucket API token"
|
|
38
|
+
private: true
|
|
39
|
+
|
|
40
|
+
roles:
|
|
41
|
+
- onboard_ca
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
---
|
|
2
|
+
# Pinned published antelope-cli version that provides the `onboarding-ca` command.
|
|
3
|
+
antelope_cli_version: "1.2.0"
|
|
4
|
+
|
|
5
|
+
# Where web-client-area is cloned on the control node.
|
|
6
|
+
work_dir: "/tmp/onboard-ca/web-client-area"
|
|
7
|
+
|
|
8
|
+
# Brand assets folder name (used for ENV_branding.brand_assets_folder and the src/assets/other/<folder>).
|
|
9
|
+
# Empty => derived from brand_name (lowercased, spaces removed), matching antelope-cli's
|
|
10
|
+
# connectedBrandName so the folder name is identical across repos.
|
|
11
|
+
brand_assets_folder: ""
|
|
12
|
+
|
|
13
|
+
# Reviewers for the PR. Empty => auto-fetched from the repo's Bitbucket "Default reviewers"
|
|
14
|
+
# config (the authenticating account is excluded automatically). Override with an explicit
|
|
15
|
+
# list, e.g. [{ account_id: "..." }] or [{ uuid: "{...}" }], to bypass the lookup.
|
|
16
|
+
reviewers: []
|
|
17
|
+
|
|
18
|
+
# Identity used for the commit (control-node git may have no global config).
|
|
19
|
+
git_user_name: "Antelope Onboarding Bot"
|
|
20
|
+
git_user_email: "devops@antelopesystem.com"
|
|
21
|
+
|
|
22
|
+
# Bitbucket API token (Bearer) for the reviewer lookup + PR creation.
|
|
23
|
+
# Provide via --extra-vars or Ansible Vault; never commit a real value.
|
|
24
|
+
bitbucket_token: ""
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
---
|
|
2
|
+
galaxy_info:
|
|
3
|
+
role_name: onboard_ca
|
|
4
|
+
author: Antelope Systems
|
|
5
|
+
description: >-
|
|
6
|
+
Onboard a Client Area brand: create the brand assets in web-client-area
|
|
7
|
+
(via antelope-cli onboarding-ca) and open the pull request(s).
|
|
8
|
+
license: ISC
|
|
9
|
+
min_ansible_version: "2.12"
|
|
10
|
+
dependencies: []
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
---
|
|
2
|
+
- name: Validate required inputs
|
|
3
|
+
ansible.builtin.assert:
|
|
4
|
+
that:
|
|
5
|
+
- brand_name is defined and (brand_name | length) > 0
|
|
6
|
+
- jira_key is defined and (jira_key | length) > 0
|
|
7
|
+
- dest_branches is defined and (dest_branches | length) > 0
|
|
8
|
+
- bitbucket_token is defined and (bitbucket_token | length) > 0
|
|
9
|
+
fail_msg: "brand_name, jira_key, dest_branches and bitbucket_token are all required."
|
|
10
|
+
|
|
11
|
+
- name: Compute effective brand assets folder
|
|
12
|
+
ansible.builtin.set_fact:
|
|
13
|
+
brand_assets_folder: >-
|
|
14
|
+
{{ brand_assets_folder
|
|
15
|
+
if (brand_assets_folder | default('') | length) > 0
|
|
16
|
+
else (brand_name | lower | regex_replace(' ', '')) }}
|
|
17
|
+
|
|
18
|
+
- name: Normalize destination branch list
|
|
19
|
+
ansible.builtin.set_fact:
|
|
20
|
+
dest_branch_list: "{{ dest_branches.split(',') | map('trim') | reject('equalto', '') | list }}"
|
|
21
|
+
|
|
22
|
+
# --- Reviewers: pull from the repo's Bitbucket default-reviewers config -------------
|
|
23
|
+
- name: Resolve reviewers from repo default-reviewers config
|
|
24
|
+
when: (reviewers | length) == 0
|
|
25
|
+
block:
|
|
26
|
+
- name: Identify the authenticating Bitbucket account (PR author)
|
|
27
|
+
ansible.builtin.uri:
|
|
28
|
+
url: "{{ api_base }}/user"
|
|
29
|
+
headers:
|
|
30
|
+
Authorization: "Bearer {{ bitbucket_token }}"
|
|
31
|
+
return_content: true
|
|
32
|
+
register: bb_user
|
|
33
|
+
|
|
34
|
+
- name: Fetch the repository default reviewers
|
|
35
|
+
ansible.builtin.uri:
|
|
36
|
+
url: "{{ api_base }}/repositories/{{ workspace }}/{{ repo_slug }}/default-reviewers?pagelen=100"
|
|
37
|
+
headers:
|
|
38
|
+
Authorization: "Bearer {{ bitbucket_token }}"
|
|
39
|
+
return_content: true
|
|
40
|
+
register: bb_default_reviewers
|
|
41
|
+
|
|
42
|
+
# Bitbucket rejects a PR whose author is also a reviewer, so drop the authenticating account.
|
|
43
|
+
# The REST API does not auto-apply default reviewers, so we pass them explicitly.
|
|
44
|
+
# Bitbucket's PR-create reviewers array is keyed by uuid; exclude the author by account_id.
|
|
45
|
+
- name: Build reviewers list (excluding the PR author)
|
|
46
|
+
ansible.builtin.set_fact:
|
|
47
|
+
reviewers: "{{ reviewers + [{'uuid': item.uuid}] }}"
|
|
48
|
+
loop: "{{ bb_default_reviewers.json['values'] }}"
|
|
49
|
+
when: item.account_id != bb_user.json.account_id
|
|
50
|
+
loop_control:
|
|
51
|
+
label: "{{ item.display_name | default(item.uuid) }}"
|
|
52
|
+
|
|
53
|
+
- name: Ensure clone parent directory exists
|
|
54
|
+
ansible.builtin.file:
|
|
55
|
+
path: "{{ work_dir | dirname }}"
|
|
56
|
+
state: directory
|
|
57
|
+
mode: "0755"
|
|
58
|
+
|
|
59
|
+
- name: Clone / refresh web-client-area over SSH
|
|
60
|
+
ansible.builtin.git:
|
|
61
|
+
repo: "{{ repo_ssh_url }}"
|
|
62
|
+
dest: "{{ work_dir }}"
|
|
63
|
+
version: "{{ dest_branch_list[0] }}"
|
|
64
|
+
update: true
|
|
65
|
+
force: true
|
|
66
|
+
accept_hostkey: true
|
|
67
|
+
|
|
68
|
+
- name: Onboard the brand on each destination branch
|
|
69
|
+
ansible.builtin.include_tasks: per_branch.yml
|
|
70
|
+
loop: "{{ dest_branch_list }}"
|
|
71
|
+
loop_control:
|
|
72
|
+
loop_var: dest_branch
|
|
73
|
+
|
|
74
|
+
- name: Summary of pull requests
|
|
75
|
+
ansible.builtin.debug:
|
|
76
|
+
msg: "{{ pr_results | default([]) }}"
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
---
|
|
2
|
+
# Runs once per destination branch. `dest_branch` is the loop var.
|
|
3
|
+
|
|
4
|
+
- name: "[{{ dest_branch }}] Compose source branch name"
|
|
5
|
+
ansible.builtin.set_fact:
|
|
6
|
+
branch_prefix: "{{ 'hotfix' if dest_branch is match('(master|release)') else 'feature' }}"
|
|
7
|
+
|
|
8
|
+
- name: "[{{ dest_branch }}] Set source branch"
|
|
9
|
+
ansible.builtin.set_fact:
|
|
10
|
+
source_branch: "{{ branch_prefix }}/{{ jira_key }}-onboarding-{{ brand_assets_folder }}"
|
|
11
|
+
|
|
12
|
+
- name: "[{{ dest_branch }}] Fetch all remote branches"
|
|
13
|
+
ansible.builtin.command:
|
|
14
|
+
cmd: git fetch origin --prune
|
|
15
|
+
chdir: "{{ work_dir }}"
|
|
16
|
+
changed_when: false
|
|
17
|
+
|
|
18
|
+
- name: "[{{ dest_branch }}] Does the source branch already exist on origin?"
|
|
19
|
+
ansible.builtin.command:
|
|
20
|
+
cmd: "git ls-remote --exit-code --heads origin {{ source_branch }}"
|
|
21
|
+
chdir: "{{ work_dir }}"
|
|
22
|
+
register: remote_src
|
|
23
|
+
failed_when: false
|
|
24
|
+
changed_when: false
|
|
25
|
+
|
|
26
|
+
# Base the working branch on its own remote head if it already exists (idempotent re-run),
|
|
27
|
+
# otherwise on the destination branch.
|
|
28
|
+
- name: "[{{ dest_branch }}] Check out the working branch"
|
|
29
|
+
ansible.builtin.command:
|
|
30
|
+
cmd: >-
|
|
31
|
+
git checkout -B {{ source_branch }}
|
|
32
|
+
{{ ('origin/' + source_branch) if remote_src.rc == 0 else ('origin/' + dest_branch) }}
|
|
33
|
+
chdir: "{{ work_dir }}"
|
|
34
|
+
|
|
35
|
+
- name: "[{{ dest_branch }}] Generate brand files via antelope-cli"
|
|
36
|
+
ansible.builtin.command:
|
|
37
|
+
argv:
|
|
38
|
+
- npx
|
|
39
|
+
- "antelope-cli@{{ antelope_cli_version }}"
|
|
40
|
+
- onboarding-ca
|
|
41
|
+
- --brand_assets_folder
|
|
42
|
+
- "{{ brand_assets_folder }}"
|
|
43
|
+
chdir: "{{ work_dir }}"
|
|
44
|
+
environment:
|
|
45
|
+
CI: "true"
|
|
46
|
+
changed_when: true
|
|
47
|
+
|
|
48
|
+
- name: "[{{ dest_branch }}] Stage changes"
|
|
49
|
+
ansible.builtin.command:
|
|
50
|
+
cmd: git add -A
|
|
51
|
+
chdir: "{{ work_dir }}"
|
|
52
|
+
changed_when: false
|
|
53
|
+
|
|
54
|
+
- name: "[{{ dest_branch }}] Detect staged changes"
|
|
55
|
+
ansible.builtin.command:
|
|
56
|
+
cmd: git diff --cached --quiet
|
|
57
|
+
chdir: "{{ work_dir }}"
|
|
58
|
+
register: staged
|
|
59
|
+
failed_when: false
|
|
60
|
+
changed_when: false
|
|
61
|
+
|
|
62
|
+
- name: "[{{ dest_branch }}] Commit"
|
|
63
|
+
ansible.builtin.command:
|
|
64
|
+
cmd: >-
|
|
65
|
+
git -c user.name="{{ git_user_name }}" -c user.email="{{ git_user_email }}"
|
|
66
|
+
commit -m "{{ jira_key }}: On-Boarding {{ brand_name }} Client Branding"
|
|
67
|
+
chdir: "{{ work_dir }}"
|
|
68
|
+
when: staged.rc == 1
|
|
69
|
+
|
|
70
|
+
- name: "[{{ dest_branch }}] Push source branch"
|
|
71
|
+
ansible.builtin.command:
|
|
72
|
+
cmd: "git push -u origin {{ source_branch }}"
|
|
73
|
+
chdir: "{{ work_dir }}"
|
|
74
|
+
when: staged.rc == 1
|
|
75
|
+
|
|
76
|
+
- name: "[{{ dest_branch }}] Open pull request"
|
|
77
|
+
ansible.builtin.uri:
|
|
78
|
+
url: "{{ api_base }}/repositories/{{ workspace }}/{{ repo_slug }}/pullrequests"
|
|
79
|
+
method: POST
|
|
80
|
+
headers:
|
|
81
|
+
Authorization: "Bearer {{ bitbucket_token }}"
|
|
82
|
+
body_format: json
|
|
83
|
+
body:
|
|
84
|
+
title: "{{ jira_key }}: On-Boarding {{ brand_name }} Client Branding"
|
|
85
|
+
description: "{{ jira_key }}: On-Boarding {{ brand_name }} Client Branding"
|
|
86
|
+
source:
|
|
87
|
+
branch:
|
|
88
|
+
name: "{{ source_branch }}"
|
|
89
|
+
destination:
|
|
90
|
+
branch:
|
|
91
|
+
name: "{{ dest_branch }}"
|
|
92
|
+
reviewers: "{{ reviewers }}"
|
|
93
|
+
close_source_branch: true
|
|
94
|
+
return_content: true
|
|
95
|
+
status_code: [201, 400] # 400 tolerated: a PR for this source->dest already exists
|
|
96
|
+
register: pr_response
|
|
97
|
+
|
|
98
|
+
- name: "[{{ dest_branch }}] Record PR result"
|
|
99
|
+
ansible.builtin.set_fact:
|
|
100
|
+
pr_results: >-
|
|
101
|
+
{{ (pr_results | default([])) + [{
|
|
102
|
+
'destination': dest_branch,
|
|
103
|
+
'source': source_branch,
|
|
104
|
+
'status': pr_response.status,
|
|
105
|
+
'url': (pr_response.json.links.html.href | default('(already exists or not created)'))
|
|
106
|
+
}] }}
|
package/index.js
CHANGED
|
@@ -3,6 +3,10 @@ const args = process.argv.slice(2);
|
|
|
3
3
|
|
|
4
4
|
if (args[0] === "onboarding") {
|
|
5
5
|
require('./onboarding');
|
|
6
|
+
} else if (args[0] === "onboarding-ca") {
|
|
7
|
+
require('./onboarding-ca');
|
|
8
|
+
} else if (args[0] === "onboarding-crm") {
|
|
9
|
+
require('./onboarding-crm');
|
|
6
10
|
} else {
|
|
7
11
|
console.log("Unknown command");
|
|
8
12
|
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
const readline = require('readline');
|
|
2
|
+
const chalk = require('chalk');
|
|
3
|
+
const brandModule = require('./brand.js');
|
|
4
|
+
const scss = require('./scss.js');
|
|
5
|
+
const riskDisclaimer = require('./riskDisclaimer.js');
|
|
6
|
+
|
|
7
|
+
const HELP = `
|
|
8
|
+
Usage: ant-cli onboarding-ca --brand_assets_folder <folder>
|
|
9
|
+
|
|
10
|
+
Creates the Client Area brand assets in the current web-client-area checkout:
|
|
11
|
+
src/assets/other/<folder>/<folder>.scss (empty)
|
|
12
|
+
src/assets/other/<folder>/legal/risk-disclaimer.html
|
|
13
|
+
|
|
14
|
+
Options:
|
|
15
|
+
--brand_assets_folder Brand assets folder name, e.g. "zenithhorizongroup"
|
|
16
|
+
(required in non-interactive mode). This is the value used for
|
|
17
|
+
ENV_branding.brand_assets_folder. Normalized to lowercase, spaces removed.
|
|
18
|
+
-h, --help Show this help
|
|
19
|
+
|
|
20
|
+
Run from the root of a web-client-area checkout — files are written relative to the current directory.
|
|
21
|
+
`;
|
|
22
|
+
|
|
23
|
+
// Normalize to the folder convention used across the brand repos (connectedBrandName):
|
|
24
|
+
// lowercase, no spaces. Harmless when a clean folder name is already supplied.
|
|
25
|
+
const normalizeFolder = (value) => value.trim().toLowerCase().replace(/ /g, '');
|
|
26
|
+
|
|
27
|
+
function parseArgs(argv) {
|
|
28
|
+
const opts = {};
|
|
29
|
+
for (let i = 0; i < argv.length; i++) {
|
|
30
|
+
const a = argv[i];
|
|
31
|
+
if (a === '--brand_assets_folder') opts.brandAssetsFolder = argv[++i];
|
|
32
|
+
else if (a === '--help' || a === '-h') opts.help = true;
|
|
33
|
+
}
|
|
34
|
+
return opts;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function promptFolder(rl) {
|
|
38
|
+
return new Promise((resolve) => {
|
|
39
|
+
rl.question('Enter the brand assets folder name (e.g. zenithhorizongroup):\n', (name) => resolve(name));
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
async function run() {
|
|
44
|
+
// process.argv: [node, index.js, "onboarding-ca", ...flags]
|
|
45
|
+
const opts = parseArgs(process.argv.slice(3));
|
|
46
|
+
if (opts.help) {
|
|
47
|
+
console.log(HELP);
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
console.log(chalk.underline(chalk.cyan('Antelope Client-Area on-boarding\n')));
|
|
52
|
+
|
|
53
|
+
// Interactive only in a real terminal and when not forced non-interactive (CI / Ansible).
|
|
54
|
+
const interactive = Boolean(process.stdin.isTTY) && !process.env.CI;
|
|
55
|
+
|
|
56
|
+
let rl;
|
|
57
|
+
let rawFolder = opts.brandAssetsFolder;
|
|
58
|
+
if (!rawFolder) {
|
|
59
|
+
if (!interactive) {
|
|
60
|
+
console.error(chalk.red('Error: --brand_assets_folder is required in non-interactive mode.'));
|
|
61
|
+
console.log(HELP);
|
|
62
|
+
process.exitCode = 1;
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
66
|
+
rawFolder = await promptFolder(rl);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const brandAssetsFolder = normalizeFolder(rawFolder || '');
|
|
70
|
+
if (!brandAssetsFolder) {
|
|
71
|
+
console.error(chalk.red('Error: brand assets folder must not be empty.'));
|
|
72
|
+
if (rl) rl.close();
|
|
73
|
+
process.exitCode = 1;
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
brandModule.updateBrand({ brandAssetsFolder });
|
|
78
|
+
|
|
79
|
+
try {
|
|
80
|
+
console.log(chalk.bold(chalk.yellow(`\nOnboarding brand assets folder "${brandAssetsFolder}"`)));
|
|
81
|
+
console.log(chalk.bold(chalk.yellow('\nStep 1: Create brand stylesheet')));
|
|
82
|
+
await scss.start();
|
|
83
|
+
console.log(chalk.bold(chalk.yellow('\nStep 2: Create legal/risk-disclaimer.html')));
|
|
84
|
+
await riskDisclaimer.start();
|
|
85
|
+
console.log(chalk.green('\nClient-Area onboarding completed!'));
|
|
86
|
+
} catch (err) {
|
|
87
|
+
console.error(chalk.red(err) + '\n Please delete files added by this failed session.');
|
|
88
|
+
process.exitCode = 1;
|
|
89
|
+
} finally {
|
|
90
|
+
if (rl) rl.close();
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
run();
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const chalk = require('chalk');
|
|
4
|
+
const brandModule = require('./brand.js');
|
|
5
|
+
|
|
6
|
+
// The disclaimer is the same fixed markup for every brand. Read the bundled template
|
|
7
|
+
// via __dirname (resolves inside the installed package, regardless of the cwd we write to).
|
|
8
|
+
const templatePath = path.join(__dirname, 'templates', 'risk-disclaimer.html');
|
|
9
|
+
|
|
10
|
+
function start() {
|
|
11
|
+
return new Promise((resolve, reject) => {
|
|
12
|
+
try {
|
|
13
|
+
const { brandAssetsFolder } = brandModule.getBrand();
|
|
14
|
+
const dir = path.join('src', 'assets', 'other', brandAssetsFolder, 'legal');
|
|
15
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
16
|
+
const file = path.join(dir, 'risk-disclaimer.html');
|
|
17
|
+
const template = fs.readFileSync(templatePath, 'utf8');
|
|
18
|
+
fs.writeFileSync(file, template, 'utf8');
|
|
19
|
+
console.log(chalk.cyan(`Created ${file}`));
|
|
20
|
+
resolve(file);
|
|
21
|
+
} catch (error) {
|
|
22
|
+
reject(error);
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
module.exports = { start };
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const chalk = require('chalk');
|
|
4
|
+
const brandModule = require('./brand.js');
|
|
5
|
+
|
|
6
|
+
// Creates the brand stylesheet. It is intentionally EMPTY — matches every existing
|
|
7
|
+
// brand in web-client-area (git blob e69de29b). brands.ts require()s it dynamically.
|
|
8
|
+
function start() {
|
|
9
|
+
return new Promise((resolve, reject) => {
|
|
10
|
+
try {
|
|
11
|
+
const { brandAssetsFolder } = brandModule.getBrand();
|
|
12
|
+
const dir = path.join('src', 'assets', 'other', brandAssetsFolder);
|
|
13
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
14
|
+
const file = path.join(dir, `${brandAssetsFolder}.scss`);
|
|
15
|
+
fs.writeFileSync(file, '', 'utf8');
|
|
16
|
+
console.log(chalk.cyan(`Created ${file}`));
|
|
17
|
+
resolve(file);
|
|
18
|
+
} catch (error) {
|
|
19
|
+
reject(error);
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
module.exports = { start };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<div class="page-wrapper push-up">
|
|
2
|
+
<a
|
|
3
|
+
class="disclaimer__top-image push-down"
|
|
4
|
+
href="{{ 'RISK.DISCLAIMER.SPONSOR.URL' | translate }}"
|
|
5
|
+
target="_blank"
|
|
6
|
+
layout="row"
|
|
7
|
+
layout-align="center center"
|
|
8
|
+
><img alt=""
|
|
9
|
+
/></a>
|
|
10
|
+
<div class="disclaimer__title"><span translate="COMMON.RISK_DISCLAIMER"></span>:</div>
|
|
11
|
+
<p translate="DISCLAIMER" translate-value-risk="{{$ctrl.risk}}"></p>
|
|
12
|
+
</div>
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
let brand = {
|
|
2
|
+
brandName: '',
|
|
3
|
+
kebabCaseName: '',
|
|
4
|
+
connectedBrandName: '',
|
|
5
|
+
color: '',
|
|
6
|
+
languages: null,
|
|
7
|
+
favicon: '',
|
|
8
|
+
type: '',
|
|
9
|
+
FCMSenderId: '',
|
|
10
|
+
logo: ''
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
function updateBrand(updatedBrand) {
|
|
14
|
+
brand = { ...brand, ...updatedBrand };
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function getBrand() {
|
|
18
|
+
return brand;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
module.exports = { updateBrand, getBrand };
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const brandModule = require('./brand.js');
|
|
3
|
+
const chalk = require('chalk');
|
|
4
|
+
const configsFilePath = 'src/configs/configs.ts';
|
|
5
|
+
|
|
6
|
+
// Same output as onboarding/createConfig.js — FCMSenderId/type are resolved upfront (flags/prompt).
|
|
7
|
+
function start() {
|
|
8
|
+
return new Promise((resolve, reject) => {
|
|
9
|
+
fs.writeFile(configsFilePath, updateConfigFile(), 'utf8', (error) => {
|
|
10
|
+
if (error) {
|
|
11
|
+
reject(error);
|
|
12
|
+
} else {
|
|
13
|
+
console.log(chalk.cyan('configs.ts file updated successfully!'));
|
|
14
|
+
resolve(null);
|
|
15
|
+
}
|
|
16
|
+
});
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const updateConfigFile = () => {
|
|
21
|
+
const brand = brandModule.getBrand();
|
|
22
|
+
return `export const configs: WebCrmConfig = {
|
|
23
|
+
baseUrl: 'https://dev-apicrm.antelopesystem.com/SignalsCRM/',
|
|
24
|
+
appId: 0,
|
|
25
|
+
type: '${brand.type}',
|
|
26
|
+
defaultLanguage: 'en',
|
|
27
|
+
gaTrackingID: 'UA-70456753-2',
|
|
28
|
+
colorTheme: 'theme-${brand.connectedBrandName}',
|
|
29
|
+
templateTheme: 'theme-template-${brand.connectedBrandName}',
|
|
30
|
+
title: '${brand.brandName}',
|
|
31
|
+
favicon: '${brand.favicon}',
|
|
32
|
+
affiliatesApiUrl: '',
|
|
33
|
+
FCMSenderId: '${brand.FCMSenderId}',
|
|
34
|
+
brandAssetsFolder: '${brand.connectedBrandName}',
|
|
35
|
+
};`;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
module.exports = { start };
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
const readline = require('readline');
|
|
2
|
+
const chalk = require('chalk');
|
|
3
|
+
const brandModule = require('./brand.js');
|
|
4
|
+
const variablesColors = require('./variablesColors.js');
|
|
5
|
+
const themes = require('./themes.js');
|
|
6
|
+
const translations = require('./translations.js');
|
|
7
|
+
const createConfig = require('./createConfig.js');
|
|
8
|
+
|
|
9
|
+
const ALLOWED_TYPES = ['CORP_FL', 'IB_FL', 'CORP_AFF_FL', 'CORP_AFF_IB_PORTAL', 'CORP_AFF_MSQ'];
|
|
10
|
+
|
|
11
|
+
const HELP = `
|
|
12
|
+
Usage: ant-cli onboarding-crm --brand_name <name> [options]
|
|
13
|
+
|
|
14
|
+
Onboards a CRM brand in the current web-crm checkout — same edits as "onboarding", but
|
|
15
|
+
every value is taken from flags (interactive prompts are only a fallback in a TTY):
|
|
16
|
+
src/ng1/assets/css/sass/variables-colors.scss (+ $colors entry and color shades)
|
|
17
|
+
src/ng1/assets/css/sass/materialism/themes.scss (+ theme maps and .theme-template block)
|
|
18
|
+
src/assets/brands/<folder>/languages/<lang>.json (one empty file per language)
|
|
19
|
+
src/configs/configs.ts (rewritten for the brand)
|
|
20
|
+
|
|
21
|
+
Options:
|
|
22
|
+
--brand_name <name> Display name → configs title, e.g. "Zenith Horizon Group" (required)
|
|
23
|
+
--brand_assets_folder <f> Brand assets folder / connectedBrandName. Default: derived from
|
|
24
|
+
--brand_name (lowercased, spaces removed)
|
|
25
|
+
--color <hex> Brand hex color, e.g. "#1a2b3c". Empty ⇒ default palette ($corp)
|
|
26
|
+
--logo_url <url> Logo image URL (black background) (required)
|
|
27
|
+
--favicon_url <url> Favicon image URL (required)
|
|
28
|
+
--languages <csv> 2-letter language codes, comma-separated, e.g. "en,ar,de" (required)
|
|
29
|
+
--fcm_sender_id <id> Firebase FCMSenderId (required)
|
|
30
|
+
--type <type> One of: ${ALLOWED_TYPES.join(', ')} (required)
|
|
31
|
+
-h, --help Show this help
|
|
32
|
+
|
|
33
|
+
Run from the root of a web-crm checkout — files are written relative to the current directory.
|
|
34
|
+
`;
|
|
35
|
+
|
|
36
|
+
const FLAGS = {
|
|
37
|
+
'--brand_name': 'brandName',
|
|
38
|
+
'--brand_assets_folder': 'brandAssetsFolder',
|
|
39
|
+
'--color': 'color',
|
|
40
|
+
'--logo_url': 'logo',
|
|
41
|
+
'--favicon_url': 'favicon',
|
|
42
|
+
'--languages': 'languages',
|
|
43
|
+
'--fcm_sender_id': 'fcmSenderId',
|
|
44
|
+
'--type': 'type'
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
function parseArgs(argv) {
|
|
48
|
+
const opts = {};
|
|
49
|
+
for (let i = 0; i < argv.length; i++) {
|
|
50
|
+
const a = argv[i];
|
|
51
|
+
if (a === '--help' || a === '-h') opts.help = true;
|
|
52
|
+
else if (FLAGS[a]) opts[FLAGS[a]] = argv[++i];
|
|
53
|
+
}
|
|
54
|
+
return opts;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const ask = (rl, question) => new Promise((resolve) => rl.question(question, (a) => resolve(a)));
|
|
58
|
+
|
|
59
|
+
// Resolve a value: prefer the flag; otherwise prompt (interactive only). Returns undefined
|
|
60
|
+
// when absent and non-interactive, so the caller can report it as a missing required field.
|
|
61
|
+
async function resolve(opts, key, promptText, interactive, rl) {
|
|
62
|
+
if (opts[key] !== undefined) return opts[key];
|
|
63
|
+
if (interactive) return ask(rl, promptText);
|
|
64
|
+
return undefined;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
async function run() {
|
|
68
|
+
const opts = parseArgs(process.argv.slice(3));
|
|
69
|
+
if (opts.help) {
|
|
70
|
+
console.log(HELP);
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
console.log(chalk.underline(chalk.cyan('Antelope CRM on-boarding\n')));
|
|
75
|
+
|
|
76
|
+
// Interactive only in a real terminal and when not forced non-interactive (CI / Ansible).
|
|
77
|
+
const interactive = Boolean(process.stdin.isTTY) && !process.env.CI;
|
|
78
|
+
const rl = interactive ? readline.createInterface({ input: process.stdin, output: process.stdout }) : null;
|
|
79
|
+
|
|
80
|
+
try {
|
|
81
|
+
const brandName = await resolve(opts, 'brandName', "What is the new brand's name?\n", interactive, rl);
|
|
82
|
+
const kebabCaseName = brandName ? brandName.toLowerCase().replace(/ /g, '-') : '';
|
|
83
|
+
const connectedBrandName = opts.brandAssetsFolder
|
|
84
|
+
? opts.brandAssetsFolder.trim().toLowerCase().replace(/ /g, '')
|
|
85
|
+
: (brandName ? brandName.toLowerCase().replace(/ /g, '') : '');
|
|
86
|
+
|
|
87
|
+
// Color is optional (empty ⇒ default palette).
|
|
88
|
+
const color = opts.color !== undefined
|
|
89
|
+
? opts.color
|
|
90
|
+
: (interactive ? await ask(rl, `Please set a color for "${kebabCaseName}", use hex value, leave empty for default.\n`) : '');
|
|
91
|
+
|
|
92
|
+
const logo = await resolve(opts, 'logo', 'Please enter the URL to the logo image suitable for a black background:\n', interactive, rl);
|
|
93
|
+
const favicon = await resolve(opts, 'favicon', 'Please provide the url to your favicon image:\n', interactive, rl);
|
|
94
|
+
const languagesInput = await resolve(opts, 'languages', 'Enter the languages (2-letter codes) separated by commas:\n', interactive, rl);
|
|
95
|
+
const fcmSenderId = await resolve(opts, 'fcmSenderId', 'Enter the FCMSenderId value (see https://xsites.atlassian.net/wiki/spaces/IKB/pages/777650347/Onboarding+Branding+Manual+for+devs#Firebase): ', interactive, rl);
|
|
96
|
+
const typeInput = await resolve(opts, 'type', `Enter the type value (allowed values: ${ALLOWED_TYPES.join(', ')}): `, interactive, rl);
|
|
97
|
+
|
|
98
|
+
// Validate required fields.
|
|
99
|
+
const missing = [];
|
|
100
|
+
if (!brandName) missing.push('--brand_name');
|
|
101
|
+
if (!logo) missing.push('--logo_url');
|
|
102
|
+
if (!favicon) missing.push('--favicon_url');
|
|
103
|
+
if (!languagesInput) missing.push('--languages');
|
|
104
|
+
if (!fcmSenderId) missing.push('--fcm_sender_id');
|
|
105
|
+
if (!typeInput) missing.push('--type');
|
|
106
|
+
if (missing.length) {
|
|
107
|
+
console.error(chalk.red(`Error: missing required value(s): ${missing.join(', ')}`));
|
|
108
|
+
console.log(HELP);
|
|
109
|
+
process.exitCode = 1;
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const type = typeInput.toUpperCase();
|
|
114
|
+
if (!ALLOWED_TYPES.includes(type)) {
|
|
115
|
+
console.error(chalk.red(`Error: invalid --type "${typeInput}". Allowed: ${ALLOWED_TYPES.join(', ')}`));
|
|
116
|
+
process.exitCode = 1;
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const languages = languagesInput.trim().split(',').map((l) => l.trim()).filter(Boolean);
|
|
121
|
+
if (!languages.length) {
|
|
122
|
+
console.error(chalk.red('Error: --languages must contain at least one 2-letter code.'));
|
|
123
|
+
process.exitCode = 1;
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
brandModule.updateBrand({
|
|
128
|
+
brandName,
|
|
129
|
+
kebabCaseName,
|
|
130
|
+
connectedBrandName,
|
|
131
|
+
color: color || '',
|
|
132
|
+
logo,
|
|
133
|
+
favicon,
|
|
134
|
+
languages,
|
|
135
|
+
FCMSenderId: fcmSenderId,
|
|
136
|
+
type
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
console.log(chalk.bold(chalk.yellow(`\nOnboarding "${brandName}" (folder: ${connectedBrandName})`)));
|
|
140
|
+
console.log(chalk.bold(chalk.yellow('\nStep 1: Add to variables-colors.scss')));
|
|
141
|
+
await variablesColors.start();
|
|
142
|
+
console.log(chalk.bold(chalk.yellow('\nStep 2: Add to themes.scss')));
|
|
143
|
+
await themes.start();
|
|
144
|
+
console.log(chalk.bold(chalk.yellow('\nStep 3: Adding translations')));
|
|
145
|
+
await translations.start();
|
|
146
|
+
console.log(chalk.bold(chalk.yellow('\nStep 4: Editing the config file')));
|
|
147
|
+
await createConfig.start();
|
|
148
|
+
console.log(chalk.green('\nCRM onboarding completed!'));
|
|
149
|
+
} catch (err) {
|
|
150
|
+
console.error(chalk.red(err) + '\n Please delete files and code added by this failed session.');
|
|
151
|
+
process.exitCode = 1;
|
|
152
|
+
} finally {
|
|
153
|
+
if (rl) rl.close();
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
run();
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const brandModule = require('./brand.js');
|
|
3
|
+
const chalk = require('chalk');
|
|
4
|
+
const themesPath = 'src/ng1/assets/css/sass/materialism/themes.scss';
|
|
5
|
+
|
|
6
|
+
function addColorToMap(mapName, key, value, data) {
|
|
7
|
+
const mapRegex = new RegExp(`\\$${mapName}:\\s*\\(([\\s\\S]*?)\\)`);
|
|
8
|
+
const match = data.match(mapRegex);
|
|
9
|
+
if (match) {
|
|
10
|
+
const mapContent = match[1];
|
|
11
|
+
const newMapContent = `$${mapName}: (${mapContent},\n '${key}': ${value})`;
|
|
12
|
+
data = data.replace(mapRegex, newMapContent);
|
|
13
|
+
} else {
|
|
14
|
+
throw new Error(`Error: Could not find ${mapName} map in the file`);
|
|
15
|
+
}
|
|
16
|
+
return data;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// Same edits as onboarding/themes.js, driven by resolved brand state (no prompts).
|
|
20
|
+
function start() {
|
|
21
|
+
const brand = brandModule.getBrand();
|
|
22
|
+
return new Promise((resolve, reject) => {
|
|
23
|
+
const themeVar = `
|
|
24
|
+
.theme-template-${brand.connectedBrandName} {
|
|
25
|
+
#logo {
|
|
26
|
+
display: inline-block;
|
|
27
|
+
width: 240px;
|
|
28
|
+
height: 120px;
|
|
29
|
+
background: url('${brand.logo}') no-repeat center center;
|
|
30
|
+
background-size: contain;
|
|
31
|
+
}
|
|
32
|
+
.navbar-toggle-container {
|
|
33
|
+
background: #222C38;
|
|
34
|
+
|
|
35
|
+
.icon-bar {
|
|
36
|
+
background: #fff !important;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
.sidebar {
|
|
40
|
+
background: #222C38;
|
|
41
|
+
color: #ffffff;
|
|
42
|
+
|
|
43
|
+
ul a,
|
|
44
|
+
i {
|
|
45
|
+
color: #ffffff;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
.brand-logo,
|
|
49
|
+
.brand-logo-text {
|
|
50
|
+
display: none;
|
|
51
|
+
}
|
|
52
|
+
.user-logged-in:after {
|
|
53
|
+
background: #26303C;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
\n`;
|
|
58
|
+
fs.readFile(themesPath, 'utf8', (err, data) => {
|
|
59
|
+
if (err) {
|
|
60
|
+
return reject(err);
|
|
61
|
+
}
|
|
62
|
+
try {
|
|
63
|
+
data = addColorToMap(
|
|
64
|
+
'theme-colors',
|
|
65
|
+
brand.connectedBrandName,
|
|
66
|
+
brand.color ? `$${brand.connectedBrandName}` : '$corp',
|
|
67
|
+
data
|
|
68
|
+
);
|
|
69
|
+
data = addColorToMap(
|
|
70
|
+
'theme-secondary-colors',
|
|
71
|
+
brand.connectedBrandName,
|
|
72
|
+
brand.color ? `'${brand.connectedBrandName}'` : "'corp'",
|
|
73
|
+
data
|
|
74
|
+
);
|
|
75
|
+
} catch (mapError) {
|
|
76
|
+
return reject(mapError);
|
|
77
|
+
}
|
|
78
|
+
const insertionPoint = data.lastIndexOf('@each $color-name');
|
|
79
|
+
const newFileContent = data.slice(0, insertionPoint) + themeVar + data.slice(insertionPoint);
|
|
80
|
+
fs.writeFile(themesPath, newFileContent, 'utf8', (error) => {
|
|
81
|
+
if (error) {
|
|
82
|
+
reject(error);
|
|
83
|
+
} else {
|
|
84
|
+
console.log(chalk.cyan('themes.scss file updated successfully!'));
|
|
85
|
+
resolve(null);
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
module.exports = { start };
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const chalk = require('chalk');
|
|
3
|
+
const brandModule = require('./brand.js');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
|
|
6
|
+
function createTranslationsFolder(translationsDirectory) {
|
|
7
|
+
const folderPath = path.join(translationsDirectory, 'languages');
|
|
8
|
+
if (!fs.existsSync(folderPath)) {
|
|
9
|
+
fs.mkdirSync(folderPath, { recursive: true });
|
|
10
|
+
}
|
|
11
|
+
return folderPath;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function createLanguageFiles(languages, folderPath) {
|
|
15
|
+
languages.forEach((language) => {
|
|
16
|
+
const filePath = path.join(folderPath, `${language}.json`);
|
|
17
|
+
fs.writeFileSync(filePath, '{}', 'utf8');
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// Same as onboarding/translations.js, but the languages list is resolved from flags/prompt upfront.
|
|
22
|
+
function start() {
|
|
23
|
+
const brand = brandModule.getBrand();
|
|
24
|
+
const translationsDir = `src/assets/brands/${brand.connectedBrandName}`;
|
|
25
|
+
return new Promise((resolve, reject) => {
|
|
26
|
+
try {
|
|
27
|
+
const folderPath = createTranslationsFolder(translationsDir);
|
|
28
|
+
createLanguageFiles(brand.languages, folderPath);
|
|
29
|
+
console.log(chalk.cyan('Translations added successfully!'));
|
|
30
|
+
resolve(null);
|
|
31
|
+
} catch (error) {
|
|
32
|
+
reject(error + '\n Please add the translations manually.');
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
module.exports = { start };
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const brandModule = require('./brand.js');
|
|
3
|
+
const chalk = require('chalk');
|
|
4
|
+
const variablesColorsPath = 'src/ng1/assets/css/sass/variables-colors.scss';
|
|
5
|
+
const defaultColor = '$corp';
|
|
6
|
+
|
|
7
|
+
// Same edits as onboarding/variablesColors.js, driven by resolved brand state (no prompts).
|
|
8
|
+
function start() {
|
|
9
|
+
return new Promise((resolve, reject) => {
|
|
10
|
+
try {
|
|
11
|
+
const { connectedBrandName, color: hexColor } = brandModule.getBrand();
|
|
12
|
+
let data = fs.readFileSync(variablesColorsPath, 'utf8');
|
|
13
|
+
|
|
14
|
+
// Check if $colors map exists in the file
|
|
15
|
+
const colorsRegex = /\$colors:\s*\(([\s\S]*?)\)/;
|
|
16
|
+
const match = data.match(colorsRegex);
|
|
17
|
+
if (match) {
|
|
18
|
+
const colorsMap = match[1];
|
|
19
|
+
|
|
20
|
+
// Append the new brand to the colors map
|
|
21
|
+
const newColorsMap = `$colors: (${colorsMap},\n '${connectedBrandName}': ${
|
|
22
|
+
hexColor ? '$' + connectedBrandName : defaultColor
|
|
23
|
+
})`;
|
|
24
|
+
|
|
25
|
+
// Replace the $colors map in the file with the new one
|
|
26
|
+
data = data.replace(colorsRegex, newColorsMap);
|
|
27
|
+
} else {
|
|
28
|
+
return reject('Error: Could not find $colors map in the file');
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const brandVar = `\$${connectedBrandName}: (
|
|
32
|
+
'lighten-5': lighten(${hexColor}, 25%),
|
|
33
|
+
'lighten-4': lighten(${hexColor}, 20%),
|
|
34
|
+
'lighten-3': lighten(${hexColor}, 15%),
|
|
35
|
+
'lighten-2': lighten(${hexColor}, 10%),
|
|
36
|
+
'lighten-1': lighten(${hexColor}, 5%),
|
|
37
|
+
'base': ${hexColor},
|
|
38
|
+
'darken-1': darken(${hexColor}, 5%),
|
|
39
|
+
'darken-2': darken(${hexColor}, 10%),
|
|
40
|
+
'darken-3': darken(${hexColor}, 15%),
|
|
41
|
+
'darken-4': darken(${hexColor}, 20%),
|
|
42
|
+
);\n`;
|
|
43
|
+
|
|
44
|
+
const newData = hexColor ? brandVar + data : data;
|
|
45
|
+
fs.writeFileSync(variablesColorsPath, newData, 'utf8');
|
|
46
|
+
console.log(chalk.cyan('\nvariables-colors.scss file updated successfully!'));
|
|
47
|
+
resolve(null);
|
|
48
|
+
} catch (error) {
|
|
49
|
+
reject(error);
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
module.exports = { start };
|