@cakemail-org/cakemail-cli 1.7.0 → 2.0.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.
- package/.claude/settings.local.json +12 -0
- package/.env.example +40 -0
- package/.env.test.example +45 -0
- package/CHANGELOG.md +1031 -0
- package/README.md +41 -37
- package/audit-formats.js +128 -0
- package/cakemail.rb +20 -0
- package/dist/client.js +1 -1
- package/dist/client.js.map +1 -1
- package/dist/commands/account.js +1 -1
- package/dist/commands/account.js.map +1 -1
- package/dist/commands/attributes.js +1 -1
- package/dist/commands/attributes.js.map +1 -1
- package/dist/commands/campaigns.js +1 -1
- package/dist/commands/campaigns.js.map +1 -1
- package/dist/commands/contacts.js +1 -1
- package/dist/commands/contacts.js.map +1 -1
- package/dist/commands/emails.js +1 -1
- package/dist/commands/emails.js.map +1 -1
- package/dist/commands/interests.js +1 -1
- package/dist/commands/interests.js.map +1 -1
- package/dist/commands/lists.js +1 -1
- package/dist/commands/lists.js.map +1 -1
- package/dist/commands/logs.js +1 -1
- package/dist/commands/logs.js.map +1 -1
- package/dist/commands/reports.js +1 -1
- package/dist/commands/reports.js.map +1 -1
- package/dist/commands/segments.js +1 -1
- package/dist/commands/segments.js.map +1 -1
- package/dist/commands/senders.js +1 -1
- package/dist/commands/senders.js.map +1 -1
- package/dist/commands/suppressed.js +1 -1
- package/dist/commands/suppressed.js.map +1 -1
- package/dist/commands/tags.js +1 -1
- package/dist/commands/tags.js.map +1 -1
- package/dist/commands/templates.js +1 -1
- package/dist/commands/templates.js.map +1 -1
- package/dist/commands/transactional-templates.js +1 -1
- package/dist/commands/transactional-templates.js.map +1 -1
- package/dist/commands/webhooks.js +1 -1
- package/dist/commands/webhooks.js.map +1 -1
- package/dist/utils/config.js +2 -2
- package/dist/utils/config.js.map +1 -1
- package/dist/utils/errors.js +1 -1
- package/dist/utils/errors.js.map +1 -1
- package/dist/utils/progress.d.ts.map +1 -1
- package/dist/utils/progress.js +32 -4
- package/dist/utils/progress.js.map +1 -1
- package/dist/utils/spinner.d.ts +17 -0
- package/dist/utils/spinner.d.ts.map +1 -0
- package/dist/utils/spinner.js +43 -0
- package/dist/utils/spinner.js.map +1 -0
- package/docs/DOCUMENTATION-STANDARD.md +1068 -0
- package/docs/README.md +161 -0
- package/docs/developer/ARCHITECTURE.md +516 -0
- package/docs/developer/AUTH.md +204 -0
- package/docs/developer/CONTRIBUTING.md +227 -0
- package/docs/developer/DOCUMENTATION_SUMMARY.md +346 -0
- package/docs/developer/PROJECT_INDEX.md +365 -0
- package/docs/planning/API_COVERAGE.md +1045 -0
- package/docs/planning/BACKLOG.md +1159 -0
- package/docs/planning/PROFILE_SYSTEM_TASKS.md +287 -0
- package/docs/planning/UX_IMPLEMENTATION_PLAN.md +691 -0
- package/docs/planning/archive/RELEASE_CHECKLIST_v1.3.0.md +332 -0
- package/docs/planning/archive/RELEASE_v1.3.0.md +428 -0
- package/docs/planning/archive/cakemail-cli-ux-improvements.md +438 -0
- package/docs/planning/cakemail-profile-system-plan.md +1121 -0
- package/docs/testing/AI_USER_SIMULATION_DESIGN.md +1342 -0
- package/docs/testing/KENOGAMI_BIDIRECTIONAL_FLOW.md +1517 -0
- package/docs/testing/KENOGAMI_TRUTH_RECONCILIATION_SYSTEM.md +1369 -0
- package/docs/user-manual/.obsidian/app.json +1 -0
- package/docs/user-manual/.obsidian/appearance.json +1 -0
- package/docs/user-manual/.obsidian/core-plugins.json +33 -0
- package/docs/user-manual/.obsidian/workspace.json +167 -0
- package/docs/user-manual/01-getting-started/01-installation.md +214 -0
- package/docs/user-manual/01-getting-started/02-quick-start.md +432 -0
- package/docs/user-manual/01-getting-started/03-authentication.md +448 -0
- package/docs/user-manual/01-getting-started/04-configuration.md +430 -0
- package/docs/user-manual/01-getting-started/05-output-formats.md +447 -0
- package/docs/user-manual/02-core-concepts/01-accounts.md +514 -0
- package/docs/user-manual/02-core-concepts/02-profile-system.md +771 -0
- package/docs/user-manual/02-core-concepts/03-smart-defaults.md +485 -0
- package/docs/user-manual/02-core-concepts/04-authentication-methods.md +435 -0
- package/docs/user-manual/02-core-concepts/05-pagination-filtering.md +600 -0
- package/docs/user-manual/02-core-concepts/06-error-handling.md +718 -0
- package/docs/user-manual/02-core-concepts/07-api-coverage.md +483 -0
- package/docs/user-manual/03-email-operations/01-senders.md +490 -0
- package/docs/user-manual/03-email-operations/02-templates.md +444 -0
- package/docs/user-manual/03-email-operations/03-transactional-emails.md +706 -0
- package/docs/user-manual/03-email-operations/04-email-tracking.md +407 -0
- package/docs/user-manual/04-campaign-management/01-campaigns-basics.md +394 -0
- package/docs/user-manual/04-campaign-management/02-campaign-scheduling.md +630 -0
- package/docs/user-manual/04-campaign-management/03-campaign-testing.md +997 -0
- package/docs/user-manual/04-campaign-management/04-campaign-lifecycle.md +709 -0
- package/docs/user-manual/04-campaign-management/05-campaign-links.md +934 -0
- package/docs/user-manual/05-contact-management/01-lists.md +836 -0
- package/docs/user-manual/05-contact-management/02-contacts.md +1035 -0
- package/docs/user-manual/05-contact-management/03-custom-attributes.md +788 -0
- package/docs/user-manual/05-contact-management/04-segments.md +1028 -0
- package/docs/user-manual/05-contact-management/05-contact-import-export.md +1031 -0
- package/docs/user-manual/06-analytics-reporting/01-campaign-analytics.md +867 -0
- package/docs/user-manual/06-analytics-reporting/02-account-reports.md +227 -0
- package/docs/user-manual/07-integrations/01-webhooks-integration.md +259 -0
- package/docs/user-manual/07-integrations/02-automation.md +326 -0
- package/docs/user-manual/08-advanced-usage/01-scripting-patterns.md +672 -0
- package/docs/user-manual/08-advanced-usage/02-bulk-operations.md +932 -0
- package/docs/user-manual/08-advanced-usage/03-ci-cd-integration.md +892 -0
- package/docs/user-manual/08-advanced-usage/04-performance-optimization.md +766 -0
- package/docs/user-manual/09-command-reference/01-config.md +776 -0
- package/docs/user-manual/09-command-reference/02-account.md +652 -0
- package/docs/user-manual/09-command-reference/03-lists.md +958 -0
- package/docs/user-manual/09-command-reference/04-contacts.md +1408 -0
- package/docs/user-manual/09-command-reference/05-attributes.md +617 -0
- package/docs/user-manual/09-command-reference/06-segments.md +894 -0
- package/docs/user-manual/09-command-reference/07-senders.md +803 -0
- package/docs/user-manual/09-command-reference/08-templates.md +818 -0
- package/docs/user-manual/09-command-reference/09-campaigns.md +1250 -0
- package/docs/user-manual/09-command-reference/10-emails.md +807 -0
- package/docs/user-manual/09-command-reference/11-reports.md +1135 -0
- package/docs/user-manual/09-command-reference/12-webhooks.md +773 -0
- package/docs/user-manual/09-command-reference/13-suppressed.md +797 -0
- package/docs/user-manual/09-command-reference/14-interests.md +630 -0
- package/docs/user-manual/09-command-reference/15-tags.md +584 -0
- package/docs/user-manual/09-command-reference/16-logs.md +656 -0
- package/docs/user-manual/09-command-reference/17-transactional-templates.md +850 -0
- package/docs/user-manual/10-troubleshooting/01-common-errors.md +457 -0
- package/docs/user-manual/10-troubleshooting/02-authentication-issues.md +558 -0
- package/docs/user-manual/10-troubleshooting/03-connection-problems.md +634 -0
- package/docs/user-manual/10-troubleshooting/04-debugging.md +725 -0
- package/docs/user-manual/11-appendix/04-faq.md +484 -0
- package/docs/user-manual/11-appendix/05-glossary.md +250 -0
- package/docs/user-manual/README.md +0 -0
- package/package.json +13 -61
- package/src/cli.ts +125 -0
- package/src/client.ts +16 -0
- package/src/commands/account.ts +267 -0
- package/src/commands/accounts.ts +78 -0
- package/src/commands/actions.ts +249 -0
- package/src/commands/attributes.ts +139 -0
- package/src/commands/campaign-blueprints.ts +106 -0
- package/src/commands/campaigns.ts +469 -0
- package/src/commands/config.ts +77 -0
- package/src/commands/contacts.ts +612 -0
- package/src/commands/custom-attributes.ts +127 -0
- package/src/commands/dkims.ts +117 -0
- package/src/commands/domains.ts +82 -0
- package/src/commands/email-apis.ts +569 -0
- package/src/commands/emails.ts +197 -0
- package/src/commands/forms.ts +283 -0
- package/src/commands/interests.ts +155 -0
- package/src/commands/links.ts +38 -0
- package/src/commands/lists.ts +406 -0
- package/src/commands/logos.ts +71 -0
- package/src/commands/logs.ts +386 -0
- package/src/commands/reports.ts +306 -0
- package/src/commands/segments.ts +158 -0
- package/src/commands/senders.ts +204 -0
- package/src/commands/sub-accounts.ts +271 -0
- package/src/commands/suppressed-emails.ts +234 -0
- package/src/commands/suppressed.ts +198 -0
- package/src/commands/system-emails.ts +85 -0
- package/src/commands/tags.ts +146 -0
- package/src/commands/tasks.ts +116 -0
- package/src/commands/templates.ts +189 -0
- package/src/commands/tokens.ts +83 -0
- package/src/commands/transactional-emails.ts +374 -0
- package/src/commands/transactional-templates.ts +385 -0
- package/src/commands/users.ts +506 -0
- package/src/commands/webhooks.ts +172 -0
- package/src/commands/workflow-blueprints.ts +123 -0
- package/src/commands/workflows.ts +265 -0
- package/src/types/profile.ts +93 -0
- package/src/utils/auth.ts +272 -0
- package/src/utils/config-file.ts +96 -0
- package/src/utils/config.ts +134 -0
- package/src/utils/confirm.ts +32 -0
- package/src/utils/defaults.ts +99 -0
- package/src/utils/errors.ts +116 -0
- package/src/utils/interactive.ts +91 -0
- package/src/utils/list-defaults.ts +74 -0
- package/src/utils/output.ts +190 -0
- package/src/utils/progress.ts +320 -0
- package/src/utils/spinner.ts +22 -0
- package/tests/IMPLEMENTATION_STATUS.md +258 -0
- package/tests/PTY_SETUP.md +118 -0
- package/tests/PTY_TESTING_GUIDE.md +507 -0
- package/tests/README.md +244 -0
- package/tests/fixtures/api-responses/campaigns.json +34 -0
- package/tests/fixtures/test-config.json +13 -0
- package/tests/helpers/cli-runner.ts +128 -0
- package/tests/helpers/mock-server.ts +301 -0
- package/tests/helpers/pty-runner.ts +181 -0
- package/tests/integration/campaigns-real-api.test.ts +196 -0
- package/tests/integration/setup-integration.ts +50 -0
- package/tests/pty/campaigns.test.ts +241 -0
- package/tests/setup.ts +34 -0
- package/tsconfig.json +15 -0
- package/vitest.config.ts +28 -0
|
@@ -0,0 +1,892 @@
|
|
|
1
|
+
# CI/CD Integration
|
|
2
|
+
|
|
3
|
+
Integrate Cakemail CLI into continuous integration and deployment pipelines for automated email marketing workflows.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
Learn to:
|
|
8
|
+
- Integrate with GitHub Actions
|
|
9
|
+
- Configure GitLab CI pipelines
|
|
10
|
+
- Set up Jenkins jobs
|
|
11
|
+
- Use Docker containers
|
|
12
|
+
- Implement deployment strategies
|
|
13
|
+
- Handle secrets securely
|
|
14
|
+
- Test campaigns automatically
|
|
15
|
+
|
|
16
|
+
## GitHub Actions
|
|
17
|
+
|
|
18
|
+
### Basic Campaign Deployment
|
|
19
|
+
|
|
20
|
+
```yaml
|
|
21
|
+
# .github/workflows/deploy-campaign.yml
|
|
22
|
+
name: Deploy Email Campaign
|
|
23
|
+
|
|
24
|
+
on:
|
|
25
|
+
push:
|
|
26
|
+
branches: [main]
|
|
27
|
+
paths:
|
|
28
|
+
- 'campaigns/**'
|
|
29
|
+
- '.github/workflows/deploy-campaign.yml'
|
|
30
|
+
|
|
31
|
+
env:
|
|
32
|
+
LIST_ID: 123
|
|
33
|
+
SENDER_ID: 101
|
|
34
|
+
|
|
35
|
+
jobs:
|
|
36
|
+
deploy:
|
|
37
|
+
runs-on: ubuntu-latest
|
|
38
|
+
|
|
39
|
+
steps:
|
|
40
|
+
- name: Checkout code
|
|
41
|
+
uses: actions/checkout@v3
|
|
42
|
+
|
|
43
|
+
- name: Setup Node.js
|
|
44
|
+
uses: actions/setup-node@v3
|
|
45
|
+
with:
|
|
46
|
+
node-version: '18'
|
|
47
|
+
|
|
48
|
+
- name: Install Cakemail CLI
|
|
49
|
+
run: npm install -g @cakemail-org/cakemail-cli
|
|
50
|
+
|
|
51
|
+
- name: Configure Cakemail
|
|
52
|
+
env:
|
|
53
|
+
CAKEMAIL_EMAIL: ${{ secrets.CAKEMAIL_EMAIL }}
|
|
54
|
+
CAKEMAIL_PASSWORD: ${{ secrets.CAKEMAIL_PASSWORD }}
|
|
55
|
+
run: |
|
|
56
|
+
echo "CAKEMAIL_EMAIL=$CAKEMAIL_EMAIL" > .env
|
|
57
|
+
echo "CAKEMAIL_PASSWORD=$CAKEMAIL_PASSWORD" >> .env
|
|
58
|
+
|
|
59
|
+
- name: Validate HTML
|
|
60
|
+
run: |
|
|
61
|
+
npm install -g html-validator-cli
|
|
62
|
+
html-validator campaigns/*.html
|
|
63
|
+
|
|
64
|
+
- name: Create campaign
|
|
65
|
+
id: create
|
|
66
|
+
run: |
|
|
67
|
+
CAMPAIGN_ID=$(cakemail campaigns create \
|
|
68
|
+
-n "Campaign $(date +%Y-%m-%d)" \
|
|
69
|
+
-l ${{ env.LIST_ID }} \
|
|
70
|
+
-s ${{ env.SENDER_ID }} \
|
|
71
|
+
--html-file campaigns/newsletter.html \
|
|
72
|
+
--subject "$(cat campaigns/subject.txt)" \
|
|
73
|
+
-f json | jq -r '.id')
|
|
74
|
+
echo "campaign_id=$CAMPAIGN_ID" >> $GITHUB_OUTPUT
|
|
75
|
+
echo "Created campaign: $CAMPAIGN_ID"
|
|
76
|
+
|
|
77
|
+
- name: Send test email
|
|
78
|
+
run: |
|
|
79
|
+
cakemail campaigns test ${{ steps.create.outputs.campaign_id }} \
|
|
80
|
+
-e ${{ secrets.TEST_EMAIL }}
|
|
81
|
+
|
|
82
|
+
- name: Schedule campaign
|
|
83
|
+
if: github.ref == 'refs/heads/main'
|
|
84
|
+
run: |
|
|
85
|
+
cakemail campaigns schedule ${{ steps.create.outputs.campaign_id }} \
|
|
86
|
+
--when "$(date -d 'tomorrow 09:00' '+%Y-%m-%d %H:%M:%S')"
|
|
87
|
+
|
|
88
|
+
- name: Comment on commit
|
|
89
|
+
uses: actions/github-script@v6
|
|
90
|
+
with:
|
|
91
|
+
script: |
|
|
92
|
+
github.rest.repos.createCommitComment({
|
|
93
|
+
owner: context.repo.owner,
|
|
94
|
+
repo: context.repo.repo,
|
|
95
|
+
commit_sha: context.sha,
|
|
96
|
+
body: '✅ Campaign ${{ steps.create.outputs.campaign_id }} deployed successfully'
|
|
97
|
+
})
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### Multi-Environment Deployment
|
|
101
|
+
|
|
102
|
+
```yaml
|
|
103
|
+
# .github/workflows/multi-env-deploy.yml
|
|
104
|
+
name: Multi-Environment Deploy
|
|
105
|
+
|
|
106
|
+
on:
|
|
107
|
+
push:
|
|
108
|
+
branches: [develop, staging, main]
|
|
109
|
+
|
|
110
|
+
jobs:
|
|
111
|
+
deploy:
|
|
112
|
+
runs-on: ubuntu-latest
|
|
113
|
+
strategy:
|
|
114
|
+
matrix:
|
|
115
|
+
include:
|
|
116
|
+
- branch: develop
|
|
117
|
+
environment: development
|
|
118
|
+
list_id: 123
|
|
119
|
+
sender_id: 101
|
|
120
|
+
auto_send: false
|
|
121
|
+
- branch: staging
|
|
122
|
+
environment: staging
|
|
123
|
+
list_id: 124
|
|
124
|
+
sender_id: 102
|
|
125
|
+
auto_send: false
|
|
126
|
+
- branch: main
|
|
127
|
+
environment: production
|
|
128
|
+
list_id: 125
|
|
129
|
+
sender_id: 103
|
|
130
|
+
auto_send: true
|
|
131
|
+
|
|
132
|
+
steps:
|
|
133
|
+
- uses: actions/checkout@v3
|
|
134
|
+
|
|
135
|
+
- name: Setup environment
|
|
136
|
+
if: github.ref == format('refs/heads/{0}', matrix.branch)
|
|
137
|
+
run: |
|
|
138
|
+
echo "ENVIRONMENT=${{ matrix.environment }}" >> $GITHUB_ENV
|
|
139
|
+
echo "LIST_ID=${{ matrix.list_id }}" >> $GITHUB_ENV
|
|
140
|
+
echo "SENDER_ID=${{ matrix.sender_id }}" >> $GITHUB_ENV
|
|
141
|
+
|
|
142
|
+
- name: Install CLI
|
|
143
|
+
if: github.ref == format('refs/heads/{0}', matrix.branch)
|
|
144
|
+
run: npm install -g @cakemail-org/cakemail-cli
|
|
145
|
+
|
|
146
|
+
- name: Configure Cakemail
|
|
147
|
+
if: github.ref == format('refs/heads/{0}', matrix.branch)
|
|
148
|
+
env:
|
|
149
|
+
CAKEMAIL_EMAIL: ${{ secrets[format('CAKEMAIL_EMAIL_{0}', matrix.environment)] }}
|
|
150
|
+
CAKEMAIL_PASSWORD: ${{ secrets[format('CAKEMAIL_PASSWORD_{0}', matrix.environment)] }}
|
|
151
|
+
run: |
|
|
152
|
+
echo "CAKEMAIL_EMAIL=$CAKEMAIL_EMAIL" > .env
|
|
153
|
+
echo "CAKEMAIL_PASSWORD=$CAKEMAIL_PASSWORD" >> .env
|
|
154
|
+
|
|
155
|
+
- name: Deploy campaign
|
|
156
|
+
if: github.ref == format('refs/heads/{0}', matrix.branch)
|
|
157
|
+
run: |
|
|
158
|
+
echo "Deploying to ${{ matrix.environment }}"
|
|
159
|
+
CAMPAIGN_ID=$(cakemail campaigns create \
|
|
160
|
+
-n "[${{ matrix.environment }}] Newsletter" \
|
|
161
|
+
-l ${{ matrix.list_id }} \
|
|
162
|
+
-s ${{ matrix.sender_id }} \
|
|
163
|
+
--html-file campaigns/newsletter.html \
|
|
164
|
+
-f json | jq -r '.id')
|
|
165
|
+
|
|
166
|
+
if [ "${{ matrix.auto_send }}" = "true" ]; then
|
|
167
|
+
cakemail campaigns schedule "$CAMPAIGN_ID"
|
|
168
|
+
else
|
|
169
|
+
echo "Campaign created but not scheduled (manual approval required)"
|
|
170
|
+
fi
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
### Scheduled Campaign Workflow
|
|
174
|
+
|
|
175
|
+
```yaml
|
|
176
|
+
# .github/workflows/scheduled-newsletter.yml
|
|
177
|
+
name: Scheduled Newsletter
|
|
178
|
+
|
|
179
|
+
on:
|
|
180
|
+
schedule:
|
|
181
|
+
# Run every Monday at 8 AM UTC
|
|
182
|
+
- cron: '0 8 * * 1'
|
|
183
|
+
workflow_dispatch: # Allow manual trigger
|
|
184
|
+
|
|
185
|
+
jobs:
|
|
186
|
+
send-newsletter:
|
|
187
|
+
runs-on: ubuntu-latest
|
|
188
|
+
|
|
189
|
+
steps:
|
|
190
|
+
- uses: actions/checkout@v3
|
|
191
|
+
|
|
192
|
+
- name: Install CLI
|
|
193
|
+
run: npm install -g @cakemail-org/cakemail-cli
|
|
194
|
+
|
|
195
|
+
- name: Configure
|
|
196
|
+
env:
|
|
197
|
+
CAKEMAIL_EMAIL: ${{ secrets.CAKEMAIL_EMAIL }}
|
|
198
|
+
CAKEMAIL_PASSWORD: ${{ secrets.CAKEMAIL_PASSWORD }}
|
|
199
|
+
run: |
|
|
200
|
+
echo "CAKEMAIL_EMAIL=$CAKEMAIL_EMAIL" > .env
|
|
201
|
+
echo "CAKEMAIL_PASSWORD=$CAKEMAIL_PASSWORD" >> .env
|
|
202
|
+
|
|
203
|
+
- name: Generate content
|
|
204
|
+
run: |
|
|
205
|
+
# Generate dynamic content (example)
|
|
206
|
+
./scripts/generate-newsletter-content.sh > campaigns/weekly-newsletter.html
|
|
207
|
+
|
|
208
|
+
- name: Create and send
|
|
209
|
+
run: |
|
|
210
|
+
CAMPAIGN_ID=$(cakemail campaigns create \
|
|
211
|
+
-n "Weekly Newsletter $(date +%Y-%m-%d)" \
|
|
212
|
+
-l 123 \
|
|
213
|
+
-s 101 \
|
|
214
|
+
--html-file campaigns/weekly-newsletter.html \
|
|
215
|
+
--subject "This Week's Top Stories" \
|
|
216
|
+
-f json | jq -r '.id')
|
|
217
|
+
|
|
218
|
+
cakemail campaigns schedule "$CAMPAIGN_ID"
|
|
219
|
+
echo "Newsletter scheduled: $CAMPAIGN_ID"
|
|
220
|
+
|
|
221
|
+
- name: Notify Slack
|
|
222
|
+
if: always()
|
|
223
|
+
uses: slackapi/slack-github-action@v1
|
|
224
|
+
with:
|
|
225
|
+
payload: |
|
|
226
|
+
{
|
|
227
|
+
"text": "Weekly newsletter deployment: ${{ job.status }}"
|
|
228
|
+
}
|
|
229
|
+
env:
|
|
230
|
+
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
### Pull Request Preview
|
|
234
|
+
|
|
235
|
+
```yaml
|
|
236
|
+
# .github/workflows/pr-preview.yml
|
|
237
|
+
name: Campaign Preview
|
|
238
|
+
|
|
239
|
+
on:
|
|
240
|
+
pull_request:
|
|
241
|
+
paths:
|
|
242
|
+
- 'campaigns/**'
|
|
243
|
+
|
|
244
|
+
jobs:
|
|
245
|
+
preview:
|
|
246
|
+
runs-on: ubuntu-latest
|
|
247
|
+
|
|
248
|
+
steps:
|
|
249
|
+
- uses: actions/checkout@v3
|
|
250
|
+
|
|
251
|
+
- name: Install CLI
|
|
252
|
+
run: npm install -g @cakemail-org/cakemail-cli
|
|
253
|
+
|
|
254
|
+
- name: Configure
|
|
255
|
+
env:
|
|
256
|
+
CAKEMAIL_EMAIL: ${{ secrets.CAKEMAIL_EMAIL }}
|
|
257
|
+
CAKEMAIL_PASSWORD: ${{ secrets.CAKEMAIL_PASSWORD }}
|
|
258
|
+
run: |
|
|
259
|
+
echo "CAKEMAIL_EMAIL=$CAKEMAIL_EMAIL" > .env
|
|
260
|
+
echo "CAKEMAIL_PASSWORD=$CAKEMAIL_PASSWORD" >> .env
|
|
261
|
+
|
|
262
|
+
- name: Create preview campaign
|
|
263
|
+
id: preview
|
|
264
|
+
run: |
|
|
265
|
+
CAMPAIGN_ID=$(cakemail campaigns create \
|
|
266
|
+
-n "[PREVIEW] PR #${{ github.event.pull_request.number }}" \
|
|
267
|
+
-l 123 \
|
|
268
|
+
-s 101 \
|
|
269
|
+
--html-file campaigns/newsletter.html \
|
|
270
|
+
--subject "[PREVIEW] ${{ github.event.pull_request.title }}" \
|
|
271
|
+
-f json | jq -r '.id')
|
|
272
|
+
echo "campaign_id=$CAMPAIGN_ID" >> $GITHUB_OUTPUT
|
|
273
|
+
|
|
274
|
+
- name: Send test email
|
|
275
|
+
run: |
|
|
276
|
+
cakemail campaigns test ${{ steps.preview.outputs.campaign_id }} \
|
|
277
|
+
-e ${{ github.event.pull_request.user.email }}
|
|
278
|
+
|
|
279
|
+
- name: Comment on PR
|
|
280
|
+
uses: actions/github-script@v6
|
|
281
|
+
with:
|
|
282
|
+
script: |
|
|
283
|
+
github.rest.issues.createComment({
|
|
284
|
+
issue_number: context.issue.number,
|
|
285
|
+
owner: context.repo.owner,
|
|
286
|
+
repo: context.repo.repo,
|
|
287
|
+
body: `## 📧 Campaign Preview\n\nPreview campaign created: **${{ steps.preview.outputs.campaign_id }}**\n\nTest email sent to: ${context.payload.pull_request.user.email}\n\n[View in Cakemail](https://app.cakemail.com/campaigns/${{ steps.preview.outputs.campaign_id }})`
|
|
288
|
+
})
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
## GitLab CI
|
|
292
|
+
|
|
293
|
+
### Basic Pipeline
|
|
294
|
+
|
|
295
|
+
```yaml
|
|
296
|
+
# .gitlab-ci.yml
|
|
297
|
+
stages:
|
|
298
|
+
- validate
|
|
299
|
+
- build
|
|
300
|
+
- deploy
|
|
301
|
+
|
|
302
|
+
variables:
|
|
303
|
+
LIST_ID: "123"
|
|
304
|
+
SENDER_ID: "101"
|
|
305
|
+
|
|
306
|
+
before_script:
|
|
307
|
+
- npm install -g @cakemail-org/cakemail-cli
|
|
308
|
+
- echo "CAKEMAIL_EMAIL=$CAKEMAIL_EMAIL" > .env
|
|
309
|
+
- echo "CAKEMAIL_PASSWORD=$CAKEMAIL_PASSWORD" >> .env
|
|
310
|
+
|
|
311
|
+
validate:
|
|
312
|
+
stage: validate
|
|
313
|
+
script:
|
|
314
|
+
- npm install -g html-validator-cli
|
|
315
|
+
- html-validator campaigns/*.html
|
|
316
|
+
- echo "HTML validation passed"
|
|
317
|
+
only:
|
|
318
|
+
- merge_requests
|
|
319
|
+
- main
|
|
320
|
+
|
|
321
|
+
test_campaign:
|
|
322
|
+
stage: build
|
|
323
|
+
script:
|
|
324
|
+
- |
|
|
325
|
+
CAMPAIGN_ID=$(cakemail campaigns create \
|
|
326
|
+
-n "[TEST] Campaign" \
|
|
327
|
+
-l $LIST_ID \
|
|
328
|
+
-s $SENDER_ID \
|
|
329
|
+
--html-file campaigns/newsletter.html \
|
|
330
|
+
-f json | jq -r '.id')
|
|
331
|
+
cakemail campaigns test $CAMPAIGN_ID -e test@company.com
|
|
332
|
+
echo "Test email sent for campaign $CAMPAIGN_ID"
|
|
333
|
+
only:
|
|
334
|
+
- merge_requests
|
|
335
|
+
|
|
336
|
+
deploy_production:
|
|
337
|
+
stage: deploy
|
|
338
|
+
script:
|
|
339
|
+
- |
|
|
340
|
+
CAMPAIGN_ID=$(cakemail campaigns create \
|
|
341
|
+
-n "Newsletter $(date +%Y-%m-%d)" \
|
|
342
|
+
-l $LIST_ID \
|
|
343
|
+
-s $SENDER_ID \
|
|
344
|
+
--html-file campaigns/newsletter.html \
|
|
345
|
+
--subject "$(cat campaigns/subject.txt)" \
|
|
346
|
+
-f json | jq -r '.id')
|
|
347
|
+
cakemail campaigns schedule $CAMPAIGN_ID --when "$(date -d 'tomorrow 09:00' '+%Y-%m-%d %H:%M:%S')"
|
|
348
|
+
echo "Campaign $CAMPAIGN_ID scheduled"
|
|
349
|
+
only:
|
|
350
|
+
- main
|
|
351
|
+
when: manual
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
### Multi-Stage Pipeline
|
|
355
|
+
|
|
356
|
+
```yaml
|
|
357
|
+
# .gitlab-ci.yml
|
|
358
|
+
stages:
|
|
359
|
+
- test
|
|
360
|
+
- staging
|
|
361
|
+
- production
|
|
362
|
+
|
|
363
|
+
.deploy_template: &deploy_config
|
|
364
|
+
before_script:
|
|
365
|
+
- npm install -g @cakemail-org/cakemail-cli
|
|
366
|
+
- echo "CAKEMAIL_EMAIL=$CAKEMAIL_EMAIL" > .env
|
|
367
|
+
- echo "CAKEMAIL_PASSWORD=$CAKEMAIL_PASSWORD" >> .env
|
|
368
|
+
script:
|
|
369
|
+
- |
|
|
370
|
+
CAMPAIGN_ID=$(cakemail campaigns create \
|
|
371
|
+
-n "[$CI_ENVIRONMENT_NAME] Newsletter" \
|
|
372
|
+
-l $LIST_ID \
|
|
373
|
+
-s $SENDER_ID \
|
|
374
|
+
--html-file campaigns/newsletter.html \
|
|
375
|
+
-f json | jq -r '.id')
|
|
376
|
+
echo "Created campaign: $CAMPAIGN_ID"
|
|
377
|
+
|
|
378
|
+
if [ "$AUTO_SEND" = "true" ]; then
|
|
379
|
+
cakemail campaigns schedule $CAMPAIGN_ID
|
|
380
|
+
echo "Campaign scheduled"
|
|
381
|
+
fi
|
|
382
|
+
|
|
383
|
+
test:
|
|
384
|
+
<<: *deploy_config
|
|
385
|
+
stage: test
|
|
386
|
+
variables:
|
|
387
|
+
LIST_ID: "123"
|
|
388
|
+
SENDER_ID: "101"
|
|
389
|
+
AUTO_SEND: "false"
|
|
390
|
+
environment:
|
|
391
|
+
name: test
|
|
392
|
+
only:
|
|
393
|
+
- merge_requests
|
|
394
|
+
|
|
395
|
+
staging:
|
|
396
|
+
<<: *deploy_config
|
|
397
|
+
stage: staging
|
|
398
|
+
variables:
|
|
399
|
+
LIST_ID: "124"
|
|
400
|
+
SENDER_ID: "102"
|
|
401
|
+
AUTO_SEND: "false"
|
|
402
|
+
environment:
|
|
403
|
+
name: staging
|
|
404
|
+
only:
|
|
405
|
+
- develop
|
|
406
|
+
|
|
407
|
+
production:
|
|
408
|
+
<<: *deploy_config
|
|
409
|
+
stage: production
|
|
410
|
+
variables:
|
|
411
|
+
LIST_ID: "125"
|
|
412
|
+
SENDER_ID: "103"
|
|
413
|
+
AUTO_SEND: "true"
|
|
414
|
+
environment:
|
|
415
|
+
name: production
|
|
416
|
+
only:
|
|
417
|
+
- main
|
|
418
|
+
when: manual
|
|
419
|
+
```
|
|
420
|
+
|
|
421
|
+
## Jenkins
|
|
422
|
+
|
|
423
|
+
### Declarative Pipeline
|
|
424
|
+
|
|
425
|
+
```groovy
|
|
426
|
+
// Jenkinsfile
|
|
427
|
+
pipeline {
|
|
428
|
+
agent any
|
|
429
|
+
|
|
430
|
+
environment {
|
|
431
|
+
CAKEMAIL_EMAIL = credentials('cakemail-email')
|
|
432
|
+
CAKEMAIL_PASSWORD = credentials('cakemail-password')
|
|
433
|
+
LIST_ID = '123'
|
|
434
|
+
SENDER_ID = '101'
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
stages {
|
|
438
|
+
stage('Setup') {
|
|
439
|
+
steps {
|
|
440
|
+
sh 'npm install -g @cakemail-org/cakemail-cli'
|
|
441
|
+
sh '''
|
|
442
|
+
echo "CAKEMAIL_EMAIL=$CAKEMAIL_EMAIL" > .env
|
|
443
|
+
echo "CAKEMAIL_PASSWORD=$CAKEMAIL_PASSWORD" >> .env
|
|
444
|
+
'''
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
stage('Validate') {
|
|
449
|
+
steps {
|
|
450
|
+
sh 'npm install -g html-validator-cli'
|
|
451
|
+
sh 'html-validator campaigns/*.html'
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
stage('Create Campaign') {
|
|
456
|
+
steps {
|
|
457
|
+
script {
|
|
458
|
+
def campaignId = sh(
|
|
459
|
+
script: '''
|
|
460
|
+
cakemail campaigns create \
|
|
461
|
+
-n "Jenkins Campaign ${BUILD_NUMBER}" \
|
|
462
|
+
-l ${LIST_ID} \
|
|
463
|
+
-s ${SENDER_ID} \
|
|
464
|
+
--html-file campaigns/newsletter.html \
|
|
465
|
+
-f json | jq -r '.id'
|
|
466
|
+
''',
|
|
467
|
+
returnStdout: true
|
|
468
|
+
).trim()
|
|
469
|
+
|
|
470
|
+
env.CAMPAIGN_ID = campaignId
|
|
471
|
+
echo "Created campaign: ${campaignId}"
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
stage('Test') {
|
|
477
|
+
steps {
|
|
478
|
+
sh 'cakemail campaigns test ${CAMPAIGN_ID} -e test@company.com'
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
stage('Deploy') {
|
|
483
|
+
when {
|
|
484
|
+
branch 'main'
|
|
485
|
+
}
|
|
486
|
+
steps {
|
|
487
|
+
input message: 'Deploy campaign?', ok: 'Deploy'
|
|
488
|
+
sh 'cakemail campaigns schedule ${CAMPAIGN_ID}'
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
post {
|
|
494
|
+
success {
|
|
495
|
+
slackSend(
|
|
496
|
+
color: 'good',
|
|
497
|
+
message: "Campaign ${env.CAMPAIGN_ID} deployed successfully"
|
|
498
|
+
)
|
|
499
|
+
}
|
|
500
|
+
failure {
|
|
501
|
+
slackSend(
|
|
502
|
+
color: 'danger',
|
|
503
|
+
message: "Campaign deployment failed"
|
|
504
|
+
)
|
|
505
|
+
}
|
|
506
|
+
always {
|
|
507
|
+
cleanWs()
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
```
|
|
512
|
+
|
|
513
|
+
### Parameterized Build
|
|
514
|
+
|
|
515
|
+
```groovy
|
|
516
|
+
// Jenkinsfile
|
|
517
|
+
pipeline {
|
|
518
|
+
agent any
|
|
519
|
+
|
|
520
|
+
parameters {
|
|
521
|
+
choice(
|
|
522
|
+
name: 'ENVIRONMENT',
|
|
523
|
+
choices: ['test', 'staging', 'production'],
|
|
524
|
+
description: 'Target environment'
|
|
525
|
+
)
|
|
526
|
+
string(
|
|
527
|
+
name: 'CAMPAIGN_NAME',
|
|
528
|
+
defaultValue: 'Newsletter',
|
|
529
|
+
description: 'Campaign name'
|
|
530
|
+
)
|
|
531
|
+
booleanParam(
|
|
532
|
+
name: 'AUTO_SCHEDULE',
|
|
533
|
+
defaultValue: false,
|
|
534
|
+
description: 'Automatically schedule campaign'
|
|
535
|
+
)
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
environment {
|
|
539
|
+
CAKEMAIL_EMAIL = credentials("cakemail-email-${params.ENVIRONMENT}")
|
|
540
|
+
CAKEMAIL_PASSWORD = credentials("cakemail-password-${params.ENVIRONMENT}")
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
stages {
|
|
544
|
+
stage('Setup') {
|
|
545
|
+
steps {
|
|
546
|
+
sh 'npm install -g @cakemail-org/cakemail-cli'
|
|
547
|
+
sh '''
|
|
548
|
+
echo "CAKEMAIL_EMAIL=$CAKEMAIL_EMAIL" > .env
|
|
549
|
+
echo "CAKEMAIL_PASSWORD=$CAKEMAIL_PASSWORD" >> .env
|
|
550
|
+
'''
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
stage('Get Environment Config') {
|
|
555
|
+
steps {
|
|
556
|
+
script {
|
|
557
|
+
def config = [
|
|
558
|
+
test: [list_id: '123', sender_id: '101'],
|
|
559
|
+
staging: [list_id: '124', sender_id: '102'],
|
|
560
|
+
production: [list_id: '125', sender_id: '103']
|
|
561
|
+
]
|
|
562
|
+
|
|
563
|
+
env.LIST_ID = config[params.ENVIRONMENT].list_id
|
|
564
|
+
env.SENDER_ID = config[params.ENVIRONMENT].sender_id
|
|
565
|
+
|
|
566
|
+
echo "Deploying to ${params.ENVIRONMENT}"
|
|
567
|
+
echo "List ID: ${env.LIST_ID}"
|
|
568
|
+
echo "Sender ID: ${env.SENDER_ID}"
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
stage('Create Campaign') {
|
|
574
|
+
steps {
|
|
575
|
+
script {
|
|
576
|
+
def campaignId = sh(
|
|
577
|
+
script: """
|
|
578
|
+
cakemail campaigns create \
|
|
579
|
+
-n '[${params.ENVIRONMENT}] ${params.CAMPAIGN_NAME}' \
|
|
580
|
+
-l ${env.LIST_ID} \
|
|
581
|
+
-s ${env.SENDER_ID} \
|
|
582
|
+
--html-file campaigns/newsletter.html \
|
|
583
|
+
-f json | jq -r '.id'
|
|
584
|
+
""",
|
|
585
|
+
returnStdout: true
|
|
586
|
+
).trim()
|
|
587
|
+
|
|
588
|
+
env.CAMPAIGN_ID = campaignId
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
stage('Schedule') {
|
|
594
|
+
when {
|
|
595
|
+
expression { params.AUTO_SCHEDULE == true }
|
|
596
|
+
}
|
|
597
|
+
steps {
|
|
598
|
+
sh 'cakemail campaigns schedule ${CAMPAIGN_ID}'
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
```
|
|
604
|
+
|
|
605
|
+
## Docker Integration
|
|
606
|
+
|
|
607
|
+
### Dockerfile
|
|
608
|
+
|
|
609
|
+
```dockerfile
|
|
610
|
+
# Dockerfile
|
|
611
|
+
FROM node:18-alpine
|
|
612
|
+
|
|
613
|
+
# Install Cakemail CLI
|
|
614
|
+
RUN npm install -g @cakemail-org/cakemail-cli
|
|
615
|
+
|
|
616
|
+
# Install additional tools
|
|
617
|
+
RUN apk add --no-cache \
|
|
618
|
+
bash \
|
|
619
|
+
jq \
|
|
620
|
+
curl
|
|
621
|
+
|
|
622
|
+
# Set working directory
|
|
623
|
+
WORKDIR /workspace
|
|
624
|
+
|
|
625
|
+
# Copy campaign files
|
|
626
|
+
COPY campaigns/ ./campaigns/
|
|
627
|
+
COPY scripts/ ./scripts/
|
|
628
|
+
|
|
629
|
+
# Set entrypoint
|
|
630
|
+
ENTRYPOINT ["bash"]
|
|
631
|
+
```
|
|
632
|
+
|
|
633
|
+
### Docker Compose Workflow
|
|
634
|
+
|
|
635
|
+
```yaml
|
|
636
|
+
# docker-compose.yml
|
|
637
|
+
version: '3.8'
|
|
638
|
+
|
|
639
|
+
services:
|
|
640
|
+
cakemail-cli:
|
|
641
|
+
build: .
|
|
642
|
+
environment:
|
|
643
|
+
- CAKEMAIL_EMAIL=${CAKEMAIL_EMAIL}
|
|
644
|
+
- CAKEMAIL_PASSWORD=${CAKEMAIL_PASSWORD}
|
|
645
|
+
volumes:
|
|
646
|
+
- ./campaigns:/workspace/campaigns
|
|
647
|
+
- ./scripts:/workspace/scripts
|
|
648
|
+
- ./reports:/workspace/reports
|
|
649
|
+
command: /workspace/scripts/deploy-campaign.sh
|
|
650
|
+
|
|
651
|
+
scheduled-newsletter:
|
|
652
|
+
build: .
|
|
653
|
+
environment:
|
|
654
|
+
- CAKEMAIL_EMAIL=${CAKEMAIL_EMAIL}
|
|
655
|
+
- CAKEMAIL_PASSWORD=${CAKEMAIL_PASSWORD}
|
|
656
|
+
volumes:
|
|
657
|
+
- ./campaigns:/workspace/campaigns
|
|
658
|
+
command: /workspace/scripts/send-newsletter.sh
|
|
659
|
+
deploy:
|
|
660
|
+
restart_policy:
|
|
661
|
+
condition: none
|
|
662
|
+
```
|
|
663
|
+
|
|
664
|
+
### Docker Run Examples
|
|
665
|
+
|
|
666
|
+
```bash
|
|
667
|
+
# One-off campaign deployment
|
|
668
|
+
docker run --rm \
|
|
669
|
+
-e CAKEMAIL_EMAIL="your@email.com" \
|
|
670
|
+
-e CAKEMAIL_PASSWORD="password" \
|
|
671
|
+
-v $(pwd)/campaigns:/workspace/campaigns \
|
|
672
|
+
cakemail-cli:latest \
|
|
673
|
+
-c "cakemail campaigns create -n 'Docker Campaign' -l 123 -s 101 --html-file campaigns/newsletter.html"
|
|
674
|
+
|
|
675
|
+
# Scheduled report generation
|
|
676
|
+
docker run --rm \
|
|
677
|
+
-e CAKEMAIL_EMAIL="your@email.com" \
|
|
678
|
+
-e CAKEMAIL_PASSWORD="password" \
|
|
679
|
+
-v $(pwd)/reports:/workspace/reports \
|
|
680
|
+
cakemail-cli:latest \
|
|
681
|
+
/workspace/scripts/generate-reports.sh
|
|
682
|
+
```
|
|
683
|
+
|
|
684
|
+
## CircleCI
|
|
685
|
+
|
|
686
|
+
### Basic Configuration
|
|
687
|
+
|
|
688
|
+
```yaml
|
|
689
|
+
# .circleci/config.yml
|
|
690
|
+
version: 2.1
|
|
691
|
+
|
|
692
|
+
executors:
|
|
693
|
+
cakemail:
|
|
694
|
+
docker:
|
|
695
|
+
- image: node:18
|
|
696
|
+
working_directory: ~/project
|
|
697
|
+
|
|
698
|
+
jobs:
|
|
699
|
+
deploy-campaign:
|
|
700
|
+
executor: cakemail
|
|
701
|
+
steps:
|
|
702
|
+
- checkout
|
|
703
|
+
|
|
704
|
+
- run:
|
|
705
|
+
name: Install CLI
|
|
706
|
+
command: npm install -g @cakemail-org/cakemail-cli
|
|
707
|
+
|
|
708
|
+
- run:
|
|
709
|
+
name: Configure
|
|
710
|
+
command: |
|
|
711
|
+
echo "CAKEMAIL_EMAIL=$CAKEMAIL_EMAIL" > .env
|
|
712
|
+
echo "CAKEMAIL_PASSWORD=$CAKEMAIL_PASSWORD" >> .env
|
|
713
|
+
|
|
714
|
+
- run:
|
|
715
|
+
name: Create campaign
|
|
716
|
+
command: |
|
|
717
|
+
CAMPAIGN_ID=$(cakemail campaigns create \
|
|
718
|
+
-n "Campaign $(date +%Y-%m-%d)" \
|
|
719
|
+
-l 123 \
|
|
720
|
+
-s 101 \
|
|
721
|
+
--html-file campaigns/newsletter.html \
|
|
722
|
+
-f json | jq -r '.id')
|
|
723
|
+
echo "export CAMPAIGN_ID=$CAMPAIGN_ID" >> $BASH_ENV
|
|
724
|
+
|
|
725
|
+
- run:
|
|
726
|
+
name: Test campaign
|
|
727
|
+
command: cakemail campaigns test $CAMPAIGN_ID -e test@company.com
|
|
728
|
+
|
|
729
|
+
- run:
|
|
730
|
+
name: Schedule campaign
|
|
731
|
+
command: cakemail campaigns schedule $CAMPAIGN_ID
|
|
732
|
+
|
|
733
|
+
workflows:
|
|
734
|
+
version: 2
|
|
735
|
+
deploy:
|
|
736
|
+
jobs:
|
|
737
|
+
- deploy-campaign:
|
|
738
|
+
filters:
|
|
739
|
+
branches:
|
|
740
|
+
only: main
|
|
741
|
+
context: cakemail-production
|
|
742
|
+
```
|
|
743
|
+
|
|
744
|
+
## Azure DevOps
|
|
745
|
+
|
|
746
|
+
### Pipeline Configuration
|
|
747
|
+
|
|
748
|
+
```yaml
|
|
749
|
+
# azure-pipelines.yml
|
|
750
|
+
trigger:
|
|
751
|
+
branches:
|
|
752
|
+
include:
|
|
753
|
+
- main
|
|
754
|
+
paths:
|
|
755
|
+
include:
|
|
756
|
+
- campaigns/*
|
|
757
|
+
|
|
758
|
+
pool:
|
|
759
|
+
vmImage: 'ubuntu-latest'
|
|
760
|
+
|
|
761
|
+
variables:
|
|
762
|
+
LIST_ID: 123
|
|
763
|
+
SENDER_ID: 101
|
|
764
|
+
|
|
765
|
+
steps:
|
|
766
|
+
- task: NodeTool@0
|
|
767
|
+
inputs:
|
|
768
|
+
versionSpec: '18.x'
|
|
769
|
+
displayName: 'Install Node.js'
|
|
770
|
+
|
|
771
|
+
- script: |
|
|
772
|
+
npm install -g @cakemail-org/cakemail-cli
|
|
773
|
+
displayName: 'Install Cakemail CLI'
|
|
774
|
+
|
|
775
|
+
- task: Bash@3
|
|
776
|
+
inputs:
|
|
777
|
+
targetType: 'inline'
|
|
778
|
+
script: |
|
|
779
|
+
echo "CAKEMAIL_EMAIL=$(CAKEMAIL_EMAIL)" > .env
|
|
780
|
+
echo "CAKEMAIL_PASSWORD=$(CAKEMAIL_PASSWORD)" >> .env
|
|
781
|
+
displayName: 'Configure credentials'
|
|
782
|
+
env:
|
|
783
|
+
CAKEMAIL_EMAIL: $(CAKEMAIL_EMAIL)
|
|
784
|
+
CAKEMAIL_PASSWORD: $(CAKEMAIL_PASSWORD)
|
|
785
|
+
|
|
786
|
+
- script: |
|
|
787
|
+
CAMPAIGN_ID=$(cakemail campaigns create \
|
|
788
|
+
-n "Campaign $(date +%Y-%m-%d)" \
|
|
789
|
+
-l $(LIST_ID) \
|
|
790
|
+
-s $(SENDER_ID) \
|
|
791
|
+
--html-file campaigns/newsletter.html \
|
|
792
|
+
-f json | jq -r '.id')
|
|
793
|
+
echo "##vso[task.setvariable variable=campaignId]$CAMPAIGN_ID"
|
|
794
|
+
echo "Created campaign: $CAMPAIGN_ID"
|
|
795
|
+
displayName: 'Create campaign'
|
|
796
|
+
|
|
797
|
+
- script: |
|
|
798
|
+
cakemail campaigns test $(campaignId) -e test@company.com
|
|
799
|
+
displayName: 'Send test email'
|
|
800
|
+
|
|
801
|
+
- script: |
|
|
802
|
+
cakemail campaigns schedule $(campaignId)
|
|
803
|
+
displayName: 'Schedule campaign'
|
|
804
|
+
condition: eq(variables['Build.SourceBranch'], 'refs/heads/main')
|
|
805
|
+
```
|
|
806
|
+
|
|
807
|
+
## Best Practices
|
|
808
|
+
|
|
809
|
+
### 1. Secure Credential Management
|
|
810
|
+
|
|
811
|
+
```yaml
|
|
812
|
+
# Use encrypted secrets
|
|
813
|
+
env:
|
|
814
|
+
CAKEMAIL_EMAIL: ${{ secrets.CAKEMAIL_EMAIL }}
|
|
815
|
+
CAKEMAIL_PASSWORD: ${{ secrets.CAKEMAIL_PASSWORD }}
|
|
816
|
+
|
|
817
|
+
# Never commit credentials
|
|
818
|
+
echo ".env" >> .gitignore
|
|
819
|
+
```
|
|
820
|
+
|
|
821
|
+
### 2. Environment Separation
|
|
822
|
+
|
|
823
|
+
```bash
|
|
824
|
+
# Use different accounts/lists per environment
|
|
825
|
+
TEST_LIST_ID=123
|
|
826
|
+
STAGING_LIST_ID=124
|
|
827
|
+
PROD_LIST_ID=125
|
|
828
|
+
```
|
|
829
|
+
|
|
830
|
+
### 3. Automated Testing
|
|
831
|
+
|
|
832
|
+
```yaml
|
|
833
|
+
- name: Validate HTML
|
|
834
|
+
run: html-validator campaigns/*.html
|
|
835
|
+
|
|
836
|
+
- name: Check subject line length
|
|
837
|
+
run: |
|
|
838
|
+
SUBJECT=$(cat campaigns/subject.txt)
|
|
839
|
+
LENGTH=${#SUBJECT}
|
|
840
|
+
if [ $LENGTH -gt 60 ]; then
|
|
841
|
+
echo "Subject too long: $LENGTH chars"
|
|
842
|
+
exit 1
|
|
843
|
+
fi
|
|
844
|
+
```
|
|
845
|
+
|
|
846
|
+
### 4. Rollback Strategy
|
|
847
|
+
|
|
848
|
+
```bash
|
|
849
|
+
# Save campaign ID for rollback
|
|
850
|
+
echo "$CAMPAIGN_ID" > .last-deployment
|
|
851
|
+
|
|
852
|
+
# Rollback if needed
|
|
853
|
+
LAST_ID=$(cat .last-deployment)
|
|
854
|
+
cakemail campaigns unschedule "$LAST_ID"
|
|
855
|
+
```
|
|
856
|
+
|
|
857
|
+
### 5. Notifications
|
|
858
|
+
|
|
859
|
+
```yaml
|
|
860
|
+
- name: Notify success
|
|
861
|
+
if: success()
|
|
862
|
+
run: |
|
|
863
|
+
curl -X POST $SLACK_WEBHOOK \
|
|
864
|
+
-d '{"text":"✅ Campaign deployed"}'
|
|
865
|
+
|
|
866
|
+
- name: Notify failure
|
|
867
|
+
if: failure()
|
|
868
|
+
run: |
|
|
869
|
+
curl -X POST $SLACK_WEBHOOK \
|
|
870
|
+
-d '{"text":"❌ Campaign deployment failed"}'
|
|
871
|
+
```
|
|
872
|
+
|
|
873
|
+
### 6. Logging and Auditing
|
|
874
|
+
|
|
875
|
+
```bash
|
|
876
|
+
# Log all operations
|
|
877
|
+
exec 1> >(tee -a deploy.log)
|
|
878
|
+
exec 2>&1
|
|
879
|
+
|
|
880
|
+
echo "[$(date)] Starting deployment..."
|
|
881
|
+
```
|
|
882
|
+
|
|
883
|
+
### 7. Conditional Deployment
|
|
884
|
+
|
|
885
|
+
```yaml
|
|
886
|
+
- name: Deploy
|
|
887
|
+
if: |
|
|
888
|
+
github.ref == 'refs/heads/main' &&
|
|
889
|
+
contains(github.event.head_commit.message, '[deploy]')
|
|
890
|
+
run: ./scripts/deploy.sh
|
|
891
|
+
```
|
|
892
|
+
|