@org-press/deploy-cloudflare 0.9.12

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/LICENSE ADDED
@@ -0,0 +1,29 @@
1
+ GNU GENERAL PUBLIC LICENSE
2
+ Version 2, June 1991
3
+
4
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
5
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
6
+
7
+ Everyone is permitted to copy and distribute verbatim copies
8
+ of this license document, but changing it is not allowed.
9
+
10
+ For the full license text, see: https://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
11
+
12
+ ---
13
+
14
+ Org-Press - Static site generator for org-mode files
15
+ Copyright (C) 2024-2026
16
+
17
+ This program is free software; you can redistribute it and/or modify
18
+ it under the terms of the GNU General Public License as published by
19
+ the Free Software Foundation; either version 2 of the License, or
20
+ (at your option) any later version.
21
+
22
+ This program is distributed in the hope that it will be useful,
23
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
24
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25
+ GNU General Public License for more details.
26
+
27
+ You should have received a copy of the GNU General Public License
28
+ along with this program; if not, write to the Free Software
29
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
package/README.md ADDED
@@ -0,0 +1,239 @@
1
+ # @org-press/deploy-cloudflare
2
+
3
+ Cloudflare Pages deploy adapter for [org-press](https://orgp.dev).
4
+
5
+ Deploys static sites to Cloudflare Pages using the wrangler CLI.
6
+
7
+ ## Installation
8
+
9
+ ```bash
10
+ npm install @org-press/deploy-cloudflare
11
+ # or
12
+ pnpm add @org-press/deploy-cloudflare
13
+ ```
14
+
15
+ You also need wrangler installed:
16
+
17
+ ```bash
18
+ npm install -D wrangler
19
+ ```
20
+
21
+ ## Usage
22
+
23
+ ### Basic Usage
24
+
25
+ ```typescript
26
+ import { cloudflareAdapter } from '@org-press/deploy-cloudflare';
27
+
28
+ export default defineConfig({
29
+ deploy: {
30
+ adapter: cloudflareAdapter({
31
+ project: 'my-site',
32
+ }),
33
+ },
34
+ });
35
+ ```
36
+
37
+ ### Branch Deployments (Previews)
38
+
39
+ ```typescript
40
+ import { cloudflareAdapter } from '@org-press/deploy-cloudflare';
41
+
42
+ export default defineConfig({
43
+ deploy: {
44
+ adapter: cloudflareAdapter({
45
+ project: 'my-site',
46
+ branch: 'preview',
47
+ }),
48
+ },
49
+ });
50
+ ```
51
+
52
+ ### Full Configuration
53
+
54
+ ```typescript
55
+ import { cloudflareAdapter } from '@org-press/deploy-cloudflare';
56
+
57
+ export default defineConfig({
58
+ deploy: {
59
+ adapter: cloudflareAdapter({
60
+ // Cloudflare Pages project name (required)
61
+ project: 'my-site',
62
+
63
+ // Cloudflare account ID (optional, uses CF_ACCOUNT_ID env var if not set)
64
+ accountId: '1234567890abcdef',
65
+
66
+ // Branch for preview deployments (optional)
67
+ // If not specified, deploys to production
68
+ branch: 'preview',
69
+
70
+ // Deployment commit message (optional)
71
+ commitMessage: 'Deploy from CI',
72
+ }),
73
+ },
74
+ });
75
+ ```
76
+
77
+ ## Configuration Options
78
+
79
+ | Option | Type | Default | Description |
80
+ |--------|------|---------|-------------|
81
+ | `project` | `string` | **required** | Cloudflare Pages project name |
82
+ | `accountId` | `string` | From `CF_ACCOUNT_ID` | Cloudflare account ID |
83
+ | `branch` | `string` | - | Branch for preview deployments |
84
+ | `commitMessage` | `string` | `'Deploy from org-press'` | Deployment message shown in dashboard |
85
+
86
+ ## Environment Variables
87
+
88
+ | Variable | Description |
89
+ |----------|-------------|
90
+ | `CLOUDFLARE_API_TOKEN` | Cloudflare API token for authentication |
91
+ | `CF_API_TOKEN` | Alternative API token variable |
92
+ | `CF_ACCOUNT_ID` | Cloudflare account ID |
93
+
94
+ ## How It Works
95
+
96
+ The adapter deploys your site using the wrangler CLI:
97
+
98
+ 1. Validates wrangler is available
99
+ 2. Runs `wrangler pages deploy <outDir> --project-name <project>`
100
+ 3. Parses the deployment URL from wrangler output
101
+ 4. Returns the deployment result with URL
102
+
103
+ ## Deployment URLs
104
+
105
+ Cloudflare Pages provides different URLs based on deployment type:
106
+
107
+ - **Production**: `https://<project>.pages.dev`
108
+ - **Branch/Preview**: `https://<branch>.<project>.pages.dev`
109
+ - **Custom Domain**: Configure in Cloudflare dashboard
110
+
111
+ ## Dry Run Mode
112
+
113
+ Test your deployment configuration without actually deploying:
114
+
115
+ ```typescript
116
+ const result = await deploy({
117
+ adapter: cloudflareAdapter({ project: 'my-site' }),
118
+ dryRun: true,
119
+ });
120
+
121
+ console.log(result.url); // Predicted Cloudflare Pages URL
122
+ ```
123
+
124
+ ## Authentication
125
+
126
+ Wrangler handles authentication in several ways:
127
+
128
+ 1. **API Token** (recommended): Set `CLOUDFLARE_API_TOKEN` environment variable
129
+ 2. **OAuth**: Run `wrangler login` to authenticate interactively
130
+ 3. **Cached credentials**: Wrangler caches credentials after login
131
+
132
+ ### Creating an API Token
133
+
134
+ 1. Go to Cloudflare Dashboard > My Profile > API Tokens
135
+ 2. Create a token with "Cloudflare Pages: Edit" permission
136
+ 3. Set the token as `CLOUDFLARE_API_TOKEN` environment variable
137
+
138
+ ## CI/CD Examples
139
+
140
+ ### GitHub Actions
141
+
142
+ ```yaml
143
+ name: Deploy to Cloudflare Pages
144
+
145
+ on:
146
+ push:
147
+ branches: [main]
148
+
149
+ jobs:
150
+ deploy:
151
+ runs-on: ubuntu-latest
152
+ steps:
153
+ - uses: actions/checkout@v4
154
+
155
+ - uses: pnpm/action-setup@v2
156
+ with:
157
+ version: 8
158
+
159
+ - uses: actions/setup-node@v4
160
+ with:
161
+ node-version: 20
162
+ cache: 'pnpm'
163
+
164
+ - run: pnpm install
165
+ - run: pnpm build
166
+
167
+ - name: Deploy to Cloudflare Pages
168
+ run: pnpm deploy
169
+ env:
170
+ CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
171
+ CF_ACCOUNT_ID: ${{ secrets.CF_ACCOUNT_ID }}
172
+ ```
173
+
174
+ ### Preview Deployments on PR
175
+
176
+ ```yaml
177
+ name: Preview Deployment
178
+
179
+ on:
180
+ pull_request:
181
+ branches: [main]
182
+
183
+ jobs:
184
+ preview:
185
+ runs-on: ubuntu-latest
186
+ steps:
187
+ - uses: actions/checkout@v4
188
+
189
+ - uses: pnpm/action-setup@v2
190
+ with:
191
+ version: 8
192
+
193
+ - uses: actions/setup-node@v4
194
+ with:
195
+ node-version: 20
196
+ cache: 'pnpm'
197
+
198
+ - run: pnpm install
199
+ - run: pnpm build
200
+
201
+ - name: Deploy Preview
202
+ run: pnpm deploy --branch pr-${{ github.event.number }}
203
+ env:
204
+ CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
205
+ CF_ACCOUNT_ID: ${{ secrets.CF_ACCOUNT_ID }}
206
+ ```
207
+
208
+ ## Requirements
209
+
210
+ - Node.js 18+
211
+ - wrangler CLI (installed as dev dependency)
212
+ - Cloudflare account with Pages enabled
213
+ - API token or interactive login
214
+
215
+ ## Troubleshooting
216
+
217
+ ### "wrangler is not available"
218
+
219
+ Install wrangler as a dev dependency:
220
+
221
+ ```bash
222
+ npm install -D wrangler
223
+ ```
224
+
225
+ ### "Authentication failed"
226
+
227
+ Ensure your API token has the correct permissions:
228
+ - Cloudflare Pages: Edit
229
+ - Account: Read (if using account ID auto-detection)
230
+
231
+ ### "Project not found"
232
+
233
+ The project must exist in your Cloudflare Pages dashboard before deployment. Create it via:
234
+ - Cloudflare dashboard
235
+ - `wrangler pages project create <name>`
236
+
237
+ ## License
238
+
239
+ GPL-2.0
package/dist/index.js ADDED
@@ -0,0 +1,169 @@
1
+ // src/adapter.ts
2
+ import { spawnSync } from "child_process";
3
+ var CloudflareAdapter = class {
4
+ name = "cloudflare";
5
+ description = "Deploy to Cloudflare Pages";
6
+ config;
7
+ constructor(config) {
8
+ this.config = config;
9
+ }
10
+ /**
11
+ * Validate adapter configuration
12
+ *
13
+ * Checks:
14
+ * - wrangler is available (via npx)
15
+ * - Project name is valid
16
+ * - API token is available in environment
17
+ */
18
+ async validate(adapterConfig) {
19
+ const errors = [];
20
+ const warnings = [];
21
+ const wranglerCheck = spawnSync("npx", ["wrangler", "--version"], {
22
+ encoding: "utf-8",
23
+ timeout: 3e4
24
+ });
25
+ if (wranglerCheck.status !== 0) {
26
+ errors.push(
27
+ "wrangler is not available. Install with: npm install -D wrangler"
28
+ );
29
+ }
30
+ const project = adapterConfig.options.project || this.config.project;
31
+ if (!project) {
32
+ errors.push("Cloudflare Pages project name is required");
33
+ } else {
34
+ const projectPattern = /^[a-z0-9][a-z0-9-]*[a-z0-9]$|^[a-z0-9]$/;
35
+ if (!projectPattern.test(project)) {
36
+ errors.push(
37
+ `Invalid project name: "${project}". Must be lowercase alphanumeric with hyphens, not starting or ending with hyphen.`
38
+ );
39
+ }
40
+ }
41
+ const apiToken = adapterConfig.env.CLOUDFLARE_API_TOKEN || adapterConfig.env.CF_API_TOKEN;
42
+ if (!apiToken) {
43
+ warnings.push(
44
+ "No CLOUDFLARE_API_TOKEN or CF_API_TOKEN found. Wrangler will prompt for authentication or use cached credentials."
45
+ );
46
+ }
47
+ const accountId = adapterConfig.options.accountId || this.config.accountId || adapterConfig.env.CF_ACCOUNT_ID;
48
+ if (!accountId) {
49
+ warnings.push(
50
+ "No account ID specified. Wrangler will attempt to auto-detect or prompt for selection."
51
+ );
52
+ }
53
+ return {
54
+ valid: errors.length === 0,
55
+ errors,
56
+ warnings
57
+ };
58
+ }
59
+ /**
60
+ * Execute deployment to Cloudflare Pages
61
+ *
62
+ * Uses wrangler pages deploy command to upload and deploy the site.
63
+ */
64
+ async deploy(context) {
65
+ const { outDir, adapterConfig, dryRun, logger } = context;
66
+ const project = adapterConfig.project || this.config.project;
67
+ const branch = adapterConfig.branch || this.config.branch;
68
+ const commitMessage = adapterConfig.commitMessage || this.config.commitMessage || "Deploy from org-press";
69
+ const accountId = adapterConfig.accountId || this.config.accountId || process.env.CF_ACCOUNT_ID;
70
+ logger.info(`Deploying to Cloudflare Pages: ${project}`);
71
+ if (branch) {
72
+ logger.info(`Branch deployment: ${branch}`);
73
+ } else {
74
+ logger.info("Production deployment");
75
+ }
76
+ if (dryRun) {
77
+ logger.info("Dry run mode - skipping actual deployment");
78
+ const previewUrl = branch ? `https://${branch}.${project}.pages.dev` : `https://${project}.pages.dev`;
79
+ return {
80
+ success: true,
81
+ deploymentId: `dry-run-${Date.now()}`,
82
+ url: previewUrl,
83
+ logs: ["Dry run completed successfully"]
84
+ };
85
+ }
86
+ try {
87
+ const args = ["wrangler", "pages", "deploy", outDir];
88
+ args.push("--project-name", project);
89
+ if (branch) {
90
+ args.push("--branch", branch);
91
+ }
92
+ if (commitMessage) {
93
+ args.push("--commit-message", commitMessage);
94
+ }
95
+ const env = { ...process.env };
96
+ if (accountId) {
97
+ env.CLOUDFLARE_ACCOUNT_ID = accountId;
98
+ }
99
+ logger.info(`Running: npx ${args.join(" ")}`);
100
+ const result = spawnSync("npx", args, {
101
+ encoding: "utf-8",
102
+ timeout: 3e5,
103
+ // 5 minute timeout for uploads
104
+ env
105
+ });
106
+ if (result.status !== 0) {
107
+ const errorOutput = result.stderr || result.stdout || "Unknown error";
108
+ logger.error(`Wrangler failed: ${errorOutput}`);
109
+ return {
110
+ success: false,
111
+ error: `Wrangler deployment failed: ${errorOutput}`,
112
+ logs: result.stdout ? [result.stdout] : void 0
113
+ };
114
+ }
115
+ const output = result.stdout || "";
116
+ const url = this.parseDeploymentUrl(output, project, branch);
117
+ logger.info("Successfully deployed to Cloudflare Pages!");
118
+ if (url) {
119
+ logger.info(`Deployment URL: ${url}`);
120
+ }
121
+ return {
122
+ success: true,
123
+ deploymentId: this.parseDeploymentId(output) || `cf-${Date.now()}`,
124
+ url,
125
+ logs: output ? [output] : void 0
126
+ };
127
+ } catch (err) {
128
+ const error = err instanceof Error ? err.message : String(err);
129
+ logger.error(`Deployment failed: ${error}`);
130
+ return {
131
+ success: false,
132
+ error: `Deployment failed: ${error}`
133
+ };
134
+ }
135
+ }
136
+ /**
137
+ * Parse deployment URL from wrangler output
138
+ *
139
+ * Wrangler outputs the URL in various formats:
140
+ * - "Published to https://xxx.project.pages.dev"
141
+ * - "Deployment complete! https://xxx.project.pages.dev"
142
+ */
143
+ parseDeploymentUrl(output, project, branch) {
144
+ const urlMatch = output.match(
145
+ /https:\/\/[a-z0-9-]+(?:\.[a-z0-9-]+)*\.pages\.dev/i
146
+ );
147
+ if (urlMatch) {
148
+ return urlMatch[0];
149
+ }
150
+ if (branch) {
151
+ return `https://${branch}.${project}.pages.dev`;
152
+ }
153
+ return `https://${project}.pages.dev`;
154
+ }
155
+ /**
156
+ * Parse deployment ID from wrangler output
157
+ */
158
+ parseDeploymentId(output) {
159
+ const idMatch = output.match(/deployment[:\s]+([a-f0-9-]{36})/i);
160
+ return idMatch ? idMatch[1] : void 0;
161
+ }
162
+ };
163
+ function cloudflareAdapter(config) {
164
+ return new CloudflareAdapter(config);
165
+ }
166
+ export {
167
+ CloudflareAdapter,
168
+ cloudflareAdapter
169
+ };
package/package.json ADDED
@@ -0,0 +1,48 @@
1
+ {
2
+ "name": "@org-press/deploy-cloudflare",
3
+ "version": "0.9.12",
4
+ "description": "Cloudflare Pages deploy adapter for org-press",
5
+ "license": "GPL-2.0",
6
+ "type": "module",
7
+ "exports": {
8
+ ".": {
9
+ "types": "./dist/index.d.ts",
10
+ "import": "./dist/index.js"
11
+ }
12
+ },
13
+ "main": "./dist/index.js",
14
+ "types": "./dist/index.d.ts",
15
+ "files": [
16
+ "dist",
17
+ "src",
18
+ "README.md"
19
+ ],
20
+ "peerDependencies": {
21
+ "@org-press/deploy": ">=0.9.0"
22
+ },
23
+ "devDependencies": {
24
+ "tsup": "^8.0.0",
25
+ "typescript": "~5.7.3",
26
+ "@types/node": "^20.0.0",
27
+ "vitest": "^3.1.4",
28
+ "@org-press/deploy": "0.9.12"
29
+ },
30
+ "publishConfig": {
31
+ "access": "public"
32
+ },
33
+ "homepage": "https://orgp.dev",
34
+ "repository": {
35
+ "type": "git",
36
+ "url": "https://github.com/org-press/org-press.git"
37
+ },
38
+ "bugs": {
39
+ "url": "https://github.com/org-press/org-press/issues"
40
+ },
41
+ "scripts": {
42
+ "build": "tsup",
43
+ "dev": "tsup --watch",
44
+ "test": "vitest",
45
+ "test:ci": "vitest --run",
46
+ "clean": "rm -rf dist"
47
+ }
48
+ }