antelope-cli 1.1.6 → 1.2.0

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.
@@ -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
+ }] }}
@@ -0,0 +1,6 @@
1
+ ---
2
+ # Constants — the target repository for Client Area onboarding.
3
+ api_base: "https://api.bitbucket.org/2.0"
4
+ workspace: "xsitesinc"
5
+ repo_slug: "web-client-area"
6
+ repo_ssh_url: "git@bitbucket.org:xsitesinc/web-client-area.git"
package/index.js CHANGED
@@ -3,6 +3,8 @@ 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');
6
8
  } else {
7
9
  console.log("Unknown command");
8
10
  }
@@ -4,6 +4,7 @@ let brand = {
4
4
  connectedBrandName: '',
5
5
  color: '',
6
6
  languages: null,
7
+ favicon: '',
7
8
  type: '',
8
9
  FCMSenderId: '',
9
10
  logo: ''
@@ -42,7 +42,7 @@ const updateConfigFile = () => {
42
42
  colorTheme: 'theme-${brand.connectedBrandName}',
43
43
  templateTheme: 'theme-template-${brand.connectedBrandName}',
44
44
  title: '${brand.brandName}',
45
- favicon: '${brand.connectedBrandName}',
45
+ favicon: '${brand.favicon}',
46
46
  affiliatesApiUrl: '',
47
47
  FCMSenderId: '${brand.FCMSenderId}',
48
48
  brandAssetsFolder: '${brand.connectedBrandName}',
@@ -1,131 +1,18 @@
1
1
  const fs = require('fs');
2
2
  const brandModule = require('./brand.js');
3
3
  const chalk = require('chalk');
4
- const path = require('path');
5
- const fetch = require('node-fetch');
6
- const unzipper = require('unzipper');
7
4
 
8
5
  async function start(rl) {
9
6
  try {
10
- const brand = brandModule.getBrand();
11
- const faviconDirLocation = `src/assets/img/favicon/${brand.connectedBrandName}`;
12
-
13
- // Prompt user for the URL of the logo image
14
7
  const imagePath = await promptUser('Please provide the url to your favicon image: ', rl);
15
-
16
- // Make a POST request to the RealFaviconGenerator API
17
- console.log(chalk.inverse('Creating a favicon pack...'));
18
- const response = await fetch('https://realfavicongenerator.net/api/favicon', {
19
- method: 'POST',
20
- headers: {
21
- 'Content-Type': 'application/json'
22
- },
23
- body: JSON.stringify(getFaviconRequest(brand, imagePath))
24
- });
25
- const json = await response.json();
26
-
27
- // Handle error responses
28
- if (json.favicon_generation_result.result.error_message) {
29
- console.error(`Error generating favicon images: ${json.favicon_generation_result.result.error_message}`);
30
- return start(rl);
31
- }
32
-
33
- // Download and extract the zip file to the favicon directory
34
- await fs.promises.mkdir(faviconDirLocation, { recursive: true });
35
-
36
- const zipUrl = json.favicon_generation_result.favicon.package_url;
37
- const zipResponse = await fetch(zipUrl);
38
- const zipBuffer = await zipResponse.buffer();
39
- const zipContents = await unzipper.Open.buffer(zipBuffer);
40
- await Promise.all(
41
- zipContents.files.map(async (file) => {
42
- const content = await file.buffer();
43
- const dest = path.join(faviconDirLocation, file.path);
44
- await fs.promises.mkdir(path.dirname(dest), { recursive: true });
45
- await fs.promises.writeFile(dest, content);
46
- })
47
- );
48
-
8
+ console.log(chalk.cyan(imagePath));
9
+ brandModule.updateBrand({favicon: imagePath})
49
10
  console.log(chalk.cyan(`Favicon images generated successfully!`));
50
11
  } catch (error) {
51
12
  console.error(`Error generating favicon images: ${error}`);
52
13
  }
53
14
  }
54
15
 
55
- /**
56
- * Returns an object containing the necessary parameters for the RealFaviconGenerator API.
57
- */
58
- function getFaviconRequest(brand, imagePath) {
59
- return {
60
- favicon_generation: {
61
- api_key: '9123c4e1f216b0538089859917ff21b791a4c6db',
62
- master_picture: {
63
- type: 'url',
64
- url: imagePath
65
- },
66
- favicon_design: {
67
- ios: {
68
- picture_aspect: 'no_change',
69
- margin: '14%',
70
- manifest: {
71
- name: '',
72
- short_name: '',
73
- icons: [
74
- {
75
- src: '/apple-touch-icon-180x180.png',
76
- sizes: '192x192',
77
- type: 'image/png'
78
- }
79
- ],
80
- theme_color: '#ffffff',
81
- background_color: '#ffffff',
82
- display: 'standalone'
83
- }
84
- },
85
- desktop_browser: {},
86
- windows: {
87
- picture_aspect: 'no_change',
88
- background_color: brand.color || '#FFF',
89
- assets: {
90
- windows_80_ie10_tile: true,
91
- windows_10_ie11_edge_tiles: {
92
- small: true,
93
- medium: true,
94
- big: true,
95
- rectangle: true
96
- }
97
- },
98
- manifest: {
99
- name: '',
100
- short_name: '',
101
- icons: [
102
- {
103
- src: '/mstile-150x150.png',
104
- sizes: '150x150',
105
- type: 'image/png'
106
- }
107
- ],
108
- theme_color: '#ffffff',
109
- background_color: '#ffffff',
110
- display: 'standalone'
111
- }
112
- },
113
- safari_pinned_tab: {
114
- picture_aspect: 'silhouette',
115
- threshold: 20,
116
- theme_color: brand.color || '#FFF'
117
- }
118
- }
119
- }
120
- };
121
- }
122
-
123
- /**
124
- * Prompts the user for input with the given message.
125
- * @param {string} message The message to display to the user.
126
- * @param {readline.Interface} rl The readline interface to use for user input.
127
- * @returns {Promise<string>} A promise that resolves with the user's input.
128
- */
129
16
  function promptUser(message, rl) {
130
17
  return new Promise((resolve) => {
131
18
  rl.question(message, (input) => {
@@ -0,0 +1,13 @@
1
+ let brand = {
2
+ brandAssetsFolder: ''
3
+ };
4
+
5
+ function updateBrand(updatedBrand) {
6
+ brand = { ...brand, ...updatedBrand };
7
+ }
8
+
9
+ function getBrand() {
10
+ return brand;
11
+ }
12
+
13
+ module.exports = { updateBrand, getBrand };
@@ -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>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "antelope-cli",
3
- "version": "1.1.6",
3
+ "version": "1.2.0",
4
4
  "description": "CLI-Tool for automating processes for Antelope-Systems ",
5
5
  "main": "index.js",
6
6
  "bin": {