antelope-cli 1.2.0 → 1.2.2

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.
@@ -1,76 +1,193 @@
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([]) }}"
1
+ ---
2
+ # --- Resolve secrets: prefer the provided value, else fall back to 1Password (op CLI) ---
3
+ # The op fallback needs a signed-in `op` on the control node (op signin, or
4
+ # OP_SERVICE_ACCOUNT_TOKEN). A missing/failed op is tolerated here; the validate step
5
+ # below reports the still-empty token with a clear message.
6
+ - name: Fetch the Bitbucket API token from 1Password
7
+ when: (bitbucket_token | default('') | length) == 0
8
+ ansible.builtin.command:
9
+ argv:
10
+ - op
11
+ - item
12
+ - get
13
+ - "{{ onepassword_bitbucket_item }}"
14
+ - "--fields"
15
+ - "label={{ onepassword_bitbucket_field }}"
16
+ - "--reveal"
17
+ register: op_bitbucket
18
+ changed_when: false
19
+ failed_when: false
20
+ no_log: true
21
+
22
+ - name: Adopt the 1Password Bitbucket token
23
+ when:
24
+ - (bitbucket_token | default('') | length) == 0
25
+ - op_bitbucket is not skipped
26
+ - (op_bitbucket.rc | default(1)) == 0
27
+ - (op_bitbucket.stdout | default('') | length) > 0
28
+ ansible.builtin.set_fact:
29
+ bitbucket_token: "{{ op_bitbucket.stdout | trim }}"
30
+ no_log: true
31
+
32
+ # Consul token is only needed when destination branches are derived (no dest_branches override).
33
+ - name: Resolve the Consul token from the environment
34
+ when: (consul_token | default('') | length) == 0
35
+ ansible.builtin.set_fact:
36
+ consul_token: "{{ lookup('env', 'CONSUL_HTTP_TOKEN') }}"
37
+ no_log: true
38
+
39
+ - name: Fetch the Consul token from 1Password
40
+ when:
41
+ - (consul_token | default('') | length) == 0
42
+ - (dest_branches | default('') | length) == 0
43
+ ansible.builtin.command:
44
+ argv:
45
+ - op
46
+ - item
47
+ - get
48
+ - "{{ onepassword_consul_item }}"
49
+ - "--fields"
50
+ - "label={{ onepassword_consul_field }}"
51
+ - "--reveal"
52
+ register: op_consul
53
+ changed_when: false
54
+ failed_when: false
55
+ no_log: true
56
+
57
+ - name: Adopt the 1Password Consul token
58
+ when:
59
+ - (consul_token | default('') | length) == 0
60
+ - op_consul is not skipped
61
+ - (op_consul.rc | default(1)) == 0
62
+ - (op_consul.stdout | default('') | length) > 0
63
+ ansible.builtin.set_fact:
64
+ consul_token: "{{ op_consul.stdout | trim }}"
65
+ no_log: true
66
+
67
+ - name: Validate required inputs
68
+ ansible.builtin.assert:
69
+ that:
70
+ - brand_name is defined and (brand_name | length) > 0
71
+ - jira_key_client_branding is defined and (jira_key_client_branding | length) > 0
72
+ - bitbucket_token is defined and (bitbucket_token | length) > 0
73
+ - bitbucket_email is defined and (bitbucket_email | length) > 0
74
+ fail_msg: >-
75
+ brand_name, jira_key_client_branding and bitbucket_email are required, and bitbucket_token must be
76
+ provided (via --extra-vars / prompt) or resolvable from 1Password item
77
+ '{{ onepassword_bitbucket_item }}' field '{{ onepassword_bitbucket_field }}' (op signin).
78
+
79
+ - name: Compute effective brand assets folder
80
+ ansible.builtin.set_fact:
81
+ brand_assets_folder: >-
82
+ {{ brand_assets_folder
83
+ if (brand_assets_folder | default('') | length) > 0
84
+ else (brand_name | lower | regex_replace(' ', '')) }}
85
+
86
+ # --- Destination branches: derived from the release train in Consul ------------------
87
+ # develop is always onboarded. master-<version/prod> tracks the live release; while a new
88
+ # release is staged (version/staging != version/prod) its master-<version/staging> exists
89
+ # too and is onboarded as well. Once staging == prod (the release has shipped) there is a
90
+ # single master branch. An explicit dest_branches extra-var bypasses the lookup entirely.
91
+ - name: Resolve destination branches from Consul
92
+ when: (dest_branches | default('') | length) == 0
93
+ block:
94
+ - name: Consul token is required to derive destination branches
95
+ ansible.builtin.assert:
96
+ that:
97
+ - consul_token is defined and (consul_token | length) > 0
98
+ fail_msg: >-
99
+ consul_token is required to read version/prod and version/staging. Provide it via
100
+ -e consul_token=..., $CONSUL_HTTP_TOKEN, or 1Password item '{{ onepassword_consul_item }}'
101
+ (op signin); or pass -e dest_branches=develop,master-XX.Y to bypass the Consul lookup.
102
+
103
+ - name: Read the prod release version from Consul
104
+ ansible.builtin.uri:
105
+ url: "{{ consul_url }}/v1/kv/version/prod?raw=1"
106
+ headers:
107
+ X-Consul-Token: "{{ consul_token }}"
108
+ return_content: true
109
+ register: consul_prod
110
+
111
+ - name: Read the staging release version from Consul
112
+ ansible.builtin.uri:
113
+ url: "{{ consul_url }}/v1/kv/version/staging?raw=1"
114
+ headers:
115
+ X-Consul-Token: "{{ consul_token }}"
116
+ return_content: true
117
+ register: consul_staging
118
+
119
+ - name: Derive the destination branch list from the release versions
120
+ ansible.builtin.set_fact:
121
+ dest_branch_list: >-
122
+ {{ ['develop', 'master-' + (consul_prod.content | trim)]
123
+ + (['master-' + (consul_staging.content | trim)]
124
+ if (consul_staging.content | trim) != (consul_prod.content | trim) else []) }}
125
+
126
+ - name: Normalize destination branch list (explicit override)
127
+ when: (dest_branches | default('') | length) > 0
128
+ ansible.builtin.set_fact:
129
+ dest_branch_list: "{{ dest_branches.split(',') | map('trim') | reject('equalto', '') | list }}"
130
+
131
+ - name: Show the resolved destination branches
132
+ ansible.builtin.debug:
133
+ msg: "Destination branches: {{ dest_branch_list }}"
134
+
135
+ # --- Reviewers: pull from the repo's Bitbucket default-reviewers config -------------
136
+ - name: Resolve reviewers from repo default-reviewers config
137
+ when: (reviewers | length) == 0
138
+ block:
139
+ - name: Identify the authenticating Bitbucket account (PR author)
140
+ ansible.builtin.uri:
141
+ url: "{{ api_base }}/user"
142
+ url_username: "{{ bitbucket_email }}"
143
+ url_password: "{{ bitbucket_token }}"
144
+ force_basic_auth: true
145
+ return_content: true
146
+ register: bb_user
147
+
148
+ - name: Fetch the repository default reviewers
149
+ ansible.builtin.uri:
150
+ url: "{{ api_base }}/repositories/{{ workspace }}/{{ repo_slug }}/default-reviewers?pagelen=100"
151
+ url_username: "{{ bitbucket_email }}"
152
+ url_password: "{{ bitbucket_token }}"
153
+ force_basic_auth: true
154
+ return_content: true
155
+ register: bb_default_reviewers
156
+
157
+ # Bitbucket rejects a PR whose author is also a reviewer, so drop the authenticating account.
158
+ # The REST API does not auto-apply default reviewers, so we pass them explicitly.
159
+ # Bitbucket's PR-create reviewers array is keyed by uuid; exclude the author by account_id.
160
+ # Built in a single expression (not an accumulating set_fact loop, which is nondeterministic).
161
+ - name: Build reviewers list (excluding the PR author)
162
+ ansible.builtin.set_fact:
163
+ reviewers: >-
164
+ {{ bb_default_reviewers.json['values']
165
+ | rejectattr('account_id', 'equalto', bb_user.json.account_id)
166
+ | map(attribute='uuid')
167
+ | map('community.general.dict_kv', 'uuid')
168
+ | list }}
169
+
170
+ - name: Ensure clone parent directory exists
171
+ ansible.builtin.file:
172
+ path: "{{ work_dir | dirname }}"
173
+ state: directory
174
+ mode: "0755"
175
+
176
+ - name: Clone / refresh web-client-area over SSH
177
+ ansible.builtin.git:
178
+ repo: "{{ repo_ssh_url }}"
179
+ dest: "{{ work_dir }}"
180
+ version: "{{ dest_branch_list[0] }}"
181
+ update: true
182
+ force: true
183
+ accept_hostkey: true
184
+
185
+ - name: Onboard the brand on each destination branch
186
+ ansible.builtin.include_tasks: per_branch.yml
187
+ loop: "{{ dest_branch_list }}"
188
+ loop_control:
189
+ loop_var: dest_branch
190
+
191
+ - name: Summary of pull requests
192
+ ansible.builtin.debug:
193
+ msg: "{{ pr_results | default([]) }}"
@@ -1,106 +1,107 @@
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
- }] }}
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_client_branding }}-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_client_branding }}: 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
+ url_username: "{{ bitbucket_email }}"
81
+ url_password: "{{ bitbucket_token }}"
82
+ force_basic_auth: true
83
+ body_format: json
84
+ body:
85
+ title: "{{ jira_key_client_branding }}: On-Boarding {{ brand_name }} Client Branding"
86
+ description: "{{ jira_key_client_branding }}: On-Boarding {{ brand_name }} Client Branding"
87
+ source:
88
+ branch:
89
+ name: "{{ source_branch }}"
90
+ destination:
91
+ branch:
92
+ name: "{{ dest_branch }}"
93
+ reviewers: "{{ reviewers }}"
94
+ close_source_branch: true
95
+ return_content: true
96
+ status_code: [201, 400] # 400 tolerated: a PR for this source->dest already exists
97
+ register: pr_response
98
+
99
+ - name: "[{{ dest_branch }}] Record PR result"
100
+ ansible.builtin.set_fact:
101
+ pr_results: >-
102
+ {{ (pr_results | default([])) + [{
103
+ 'destination': dest_branch,
104
+ 'source': source_branch,
105
+ 'status': pr_response.status,
106
+ 'url': (pr_response.json.links.html.href | default('(already exists or not created)'))
107
+ }] }}
@@ -1,6 +1,9 @@
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"
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"
7
+
8
+ # Consul (v2 cluster) — source of the release train used to derive destination branches.
9
+ consul_url: "https://consul.xsites.xyz"
package/index.js CHANGED
@@ -5,6 +5,8 @@ if (args[0] === "onboarding") {
5
5
  require('./onboarding');
6
6
  } else if (args[0] === "onboarding-ca") {
7
7
  require('./onboarding-ca');
8
+ } else if (args[0] === "onboarding-crm") {
9
+ require('./onboarding-crm');
8
10
  } else {
9
11
  console.log("Unknown command");
10
12
  }
@@ -1,13 +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 };
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 };