rman 2.22.0 → 2.23.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/builtins/publish-targets/cloudflare/cloudflare-publish.service.d.ts +100 -0
- package/builtins/publish-targets/cloudflare/cloudflare-publish.service.js +213 -0
- package/builtins/publish-targets/cloudflare/cloudflare.target.d.ts +38 -0
- package/builtins/publish-targets/cloudflare/cloudflare.target.js +23 -0
- package/builtins/publish-targets/cloudflare/index.d.ts +2 -0
- package/builtins/publish-targets/cloudflare/index.js +2 -0
- package/commands/publish.command.d.ts +4 -0
- package/constants.js +1 -1
- package/core/application.js +3 -0
- package/package.json +1 -1
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import type { Package } from '../../../core/classes/package.js';
|
|
2
|
+
import { Service } from '../../../core/classes/service.js';
|
|
3
|
+
import { type PublishTarget } from '../../../core/interfaces/publish-target.js';
|
|
4
|
+
import { type PackageFilterOptions } from '../../../utils/package-filter.js';
|
|
5
|
+
/** The name this target answers to in `publish.target` and `--target`. */
|
|
6
|
+
export declare const CLOUDFLARE_TARGET = "cloudflare";
|
|
7
|
+
/**
|
|
8
|
+
* Deploys packages to Cloudflare Pages or Workers through `wrangler`, once per version.
|
|
9
|
+
*
|
|
10
|
+
* **Question B, like every target**: is *this version* already deployed? Each deploy is labelled
|
|
11
|
+
* with the version - a Pages deployment's commit message is `<name>@<version>`, a Worker version's
|
|
12
|
+
* tag is `v<version>` - and the plan reads those labels back, so a release run twice deploys once.
|
|
13
|
+
*/
|
|
14
|
+
export declare class CloudflarePublishService extends Service {
|
|
15
|
+
/** The wrangler configuration a Worker package is looked for in, in order. */
|
|
16
|
+
protected readonly workerConfigFiles: readonly string[];
|
|
17
|
+
/**
|
|
18
|
+
* What `publish --target cloudflare` would do. Only packages naming `"cloudflare"` in their own
|
|
19
|
+
* `publish.target` are candidates. A candidate whose config is incomplete, whose working tree is
|
|
20
|
+
* dirty (unless `ignoreDirty`), or that runs without `CLOUDFLARE_API_TOKEN`/`CLOUDFLARE_ACCOUNT_ID`
|
|
21
|
+
* is an `'error'`; otherwise it is `'up-to-date'` when its version is already deployed and
|
|
22
|
+
* `'publish'` when it is not.
|
|
23
|
+
*/
|
|
24
|
+
getPlan(options?: CloudflarePublishService.Options, deps?: CloudflarePublishService.Deps): Promise<CloudflarePublishService.Entry[]>;
|
|
25
|
+
/**
|
|
26
|
+
* Deploys every `'publish'` entry with `wrangler`, one package after another. A failure is that
|
|
27
|
+
* package's alone and is reported on its entry.
|
|
28
|
+
*/
|
|
29
|
+
applyPlan(plan: CloudflarePublishService.Entry[]): Promise<CloudflarePublishService.Entry[]>;
|
|
30
|
+
/** Where and how `pkg` deploys, from its `publish.cloudflare` - or a thrown reason it cannot. */
|
|
31
|
+
protected resolveDeploy(pkg: Package): CloudflarePublishService.Deploy;
|
|
32
|
+
/** The API token and account id wrangler reads, or `undefined` when either is missing. */
|
|
33
|
+
protected credentials(): {
|
|
34
|
+
token: string;
|
|
35
|
+
accountId: string;
|
|
36
|
+
} | undefined;
|
|
37
|
+
/**
|
|
38
|
+
* Whether this version is already deployed. A check that cannot be answered - the network, an
|
|
39
|
+
* expired token - reads as "not deployed", the catch-everything shape every target's registry
|
|
40
|
+
* check has: deploying a version again is harmless, failing the run over a question is not.
|
|
41
|
+
*/
|
|
42
|
+
protected isDeployed(deploy: CloudflarePublishService.Deploy): Promise<boolean>;
|
|
43
|
+
/** A Pages deployment of the project whose commit message is this version's marker. */
|
|
44
|
+
protected pagesHas(deploy: CloudflarePublishService.PagesDeploy): Promise<boolean>;
|
|
45
|
+
/** A version of the Worker tagged with this version - among the ten most recent wrangler lists. */
|
|
46
|
+
protected workerHas(deploy: CloudflarePublishService.WorkersDeploy): Promise<boolean>;
|
|
47
|
+
/** Runs the deploy, its output shown on stderr as it comes and kept for the reason if it fails. */
|
|
48
|
+
protected deploy(deploy: CloudflarePublishService.Deploy): Promise<void>;
|
|
49
|
+
/**
|
|
50
|
+
* `wrangler`, from the package's own or the repository's `node_modules` when it is installed there,
|
|
51
|
+
* and through `npx` when it is not - so a package that never added it as a dependency still
|
|
52
|
+
* deploys.
|
|
53
|
+
*/
|
|
54
|
+
protected wrangler(pkg: Package, argv: string[], onLine: (line: string, stream: 'stdout' | 'stderr') => void): Promise<void>;
|
|
55
|
+
}
|
|
56
|
+
export declare namespace CloudflarePublishService {
|
|
57
|
+
/** Injectable "is this version deployed" check, so a spec needs neither network nor wrangler. */
|
|
58
|
+
export interface Deps {
|
|
59
|
+
isDeployed?: (deploy: Deploy) => Promise<boolean>;
|
|
60
|
+
}
|
|
61
|
+
export interface Options extends PackageFilterOptions {
|
|
62
|
+
/** A package with uncommitted local changes is excluded (`'skip'`) instead of aborting the
|
|
63
|
+
* plan (`'error'`) - the same option every target takes. */
|
|
64
|
+
ignoreDirty?: boolean;
|
|
65
|
+
}
|
|
66
|
+
interface DeployBase {
|
|
67
|
+
pkg: Package;
|
|
68
|
+
/** `<name>@<version>` - the Pages commit message and the Worker version's message, and what
|
|
69
|
+
* the plan looks for on a Pages project. */
|
|
70
|
+
marker: string;
|
|
71
|
+
/** What the plan prints beside the package. */
|
|
72
|
+
label: string;
|
|
73
|
+
}
|
|
74
|
+
export interface PagesDeploy extends DeployBase {
|
|
75
|
+
kind: 'pages';
|
|
76
|
+
project: string;
|
|
77
|
+
branch: string;
|
|
78
|
+
/** Absolute. */
|
|
79
|
+
directory: string;
|
|
80
|
+
}
|
|
81
|
+
export interface WorkersDeploy extends DeployBase {
|
|
82
|
+
kind: 'workers';
|
|
83
|
+
/** `v<version>` - the Worker version's tag, which the plan looks for. */
|
|
84
|
+
tag: string;
|
|
85
|
+
/** Absolute path of the wrangler configuration. */
|
|
86
|
+
config: string;
|
|
87
|
+
}
|
|
88
|
+
export type Deploy = PagesDeploy | WorkersDeploy;
|
|
89
|
+
/** One package's outcome - a `PublishTarget.Entry` carrying where it deploys. */
|
|
90
|
+
export interface Entry extends PublishTarget.Entry {
|
|
91
|
+
/** Unset only on an `'error'` entry whose config could not be read. */
|
|
92
|
+
deploy?: Deploy;
|
|
93
|
+
}
|
|
94
|
+
export {};
|
|
95
|
+
}
|
|
96
|
+
declare module '../../../core/classes/service.js' {
|
|
97
|
+
interface ServiceMap {
|
|
98
|
+
cloudflarePublish: CloudflarePublishService;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { Service } from '../../../core/classes/service.js';
|
|
4
|
+
import { targetsOf } from '../../../core/interfaces/publish-target.js';
|
|
5
|
+
import { GitHelper } from '../../../utils/git.js';
|
|
6
|
+
import { filterPackages } from '../../../utils/package-filter.js';
|
|
7
|
+
import { runBin } from '../../../utils/run-bin.js';
|
|
8
|
+
/** The name this target answers to in `publish.target` and `--target`. */
|
|
9
|
+
export const CLOUDFLARE_TARGET = 'cloudflare';
|
|
10
|
+
/**
|
|
11
|
+
* Deploys packages to Cloudflare Pages or Workers through `wrangler`, once per version.
|
|
12
|
+
*
|
|
13
|
+
* **Question B, like every target**: is *this version* already deployed? Each deploy is labelled
|
|
14
|
+
* with the version - a Pages deployment's commit message is `<name>@<version>`, a Worker version's
|
|
15
|
+
* tag is `v<version>` - and the plan reads those labels back, so a release run twice deploys once.
|
|
16
|
+
*/
|
|
17
|
+
/* **Pages is asked through Cloudflare's API, Workers through wrangler**, and the asymmetry is
|
|
18
|
+
* wrangler's. `wrangler versions list --json` returns a version's annotations, `workers/tag`
|
|
19
|
+
* among them; `wrangler pages deployment list --json` returns id, branch, a short commit hash, url
|
|
20
|
+
* and status, and drops the commit message - measured in wrangler 4.81's own source. The API has
|
|
21
|
+
* it (`deployment_trigger.metadata.commit_message`), with the same token wrangler uses. */
|
|
22
|
+
export class CloudflarePublishService extends Service {
|
|
23
|
+
/** The wrangler configuration a Worker package is looked for in, in order. */
|
|
24
|
+
workerConfigFiles = ['wrangler.jsonc', 'wrangler.json', 'wrangler.toml'];
|
|
25
|
+
/**
|
|
26
|
+
* What `publish --target cloudflare` would do. Only packages naming `"cloudflare"` in their own
|
|
27
|
+
* `publish.target` are candidates. A candidate whose config is incomplete, whose working tree is
|
|
28
|
+
* dirty (unless `ignoreDirty`), or that runs without `CLOUDFLARE_API_TOKEN`/`CLOUDFLARE_ACCOUNT_ID`
|
|
29
|
+
* is an `'error'`; otherwise it is `'up-to-date'` when its version is already deployed and
|
|
30
|
+
* `'publish'` when it is not.
|
|
31
|
+
*/
|
|
32
|
+
async getPlan(options = {}, deps = {}) {
|
|
33
|
+
const repository = this.repository;
|
|
34
|
+
const git = new GitHelper({ cwd: repository.dirname });
|
|
35
|
+
const packages = filterPackages(repository.getPackages({ toposort: true }), options).filter(pkg => targetsOf(this.app, pkg).some(t => t.name === CLOUDFLARE_TARGET) && !pkg.config.publish?.skip);
|
|
36
|
+
const dirtyFiles = await git.listDirtyFiles({ absolute: true });
|
|
37
|
+
const isDirty = (pkg) => dirtyFiles.some(f => !path.relative(pkg.dirname, f).startsWith('..'));
|
|
38
|
+
const credentials = this.credentials();
|
|
39
|
+
const isDeployed = deps.isDeployed ?? ((deploy) => this.isDeployed(deploy));
|
|
40
|
+
return Promise.all(packages.map(async (pkg) => {
|
|
41
|
+
const base = { package: pkg, version: pkg.version };
|
|
42
|
+
let deploy;
|
|
43
|
+
try {
|
|
44
|
+
deploy = this.resolveDeploy(pkg);
|
|
45
|
+
}
|
|
46
|
+
catch (e) {
|
|
47
|
+
return { ...base, status: 'error', reason: e.message };
|
|
48
|
+
}
|
|
49
|
+
const located = { ...base, deploy, detail: deploy.label };
|
|
50
|
+
if (isDirty(pkg)) {
|
|
51
|
+
return { ...located, status: options.ignoreDirty ? 'skip' : 'error', reason: 'uncommitted local changes' };
|
|
52
|
+
}
|
|
53
|
+
if (!credentials) {
|
|
54
|
+
return {
|
|
55
|
+
...located,
|
|
56
|
+
status: 'error',
|
|
57
|
+
reason: 'CLOUDFLARE_API_TOKEN and CLOUDFLARE_ACCOUNT_ID must be set to check and deploy',
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
const deployed = await isDeployed(deploy);
|
|
61
|
+
return deployed
|
|
62
|
+
? { ...located, status: 'up-to-date', reason: `${deploy.marker} is already deployed` }
|
|
63
|
+
: { ...located, status: 'publish', reason: 'not deployed yet' };
|
|
64
|
+
}));
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Deploys every `'publish'` entry with `wrangler`, one package after another. A failure is that
|
|
68
|
+
* package's alone and is reported on its entry.
|
|
69
|
+
*/
|
|
70
|
+
async applyPlan(plan) {
|
|
71
|
+
const result = [];
|
|
72
|
+
for (const entry of plan) {
|
|
73
|
+
if (entry.status !== 'publish' || !entry.deploy) {
|
|
74
|
+
result.push(entry);
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
try {
|
|
78
|
+
await this.deploy(entry.deploy);
|
|
79
|
+
result.push(entry);
|
|
80
|
+
}
|
|
81
|
+
catch (e) {
|
|
82
|
+
result.push({ ...entry, status: 'error', reason: e.message });
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return result;
|
|
86
|
+
}
|
|
87
|
+
/** Where and how `pkg` deploys, from its `publish.cloudflare` - or a thrown reason it cannot. */
|
|
88
|
+
resolveDeploy(pkg) {
|
|
89
|
+
const cf = pkg.config.publish?.cloudflare;
|
|
90
|
+
if (!cf)
|
|
91
|
+
throw new Error('"cloudflare" is a publish target but "publish.cloudflare" is not configured');
|
|
92
|
+
const marker = `${pkg.name}@${pkg.version}`;
|
|
93
|
+
if (cf.kind === 'pages') {
|
|
94
|
+
const project = cf.project;
|
|
95
|
+
if (!project)
|
|
96
|
+
throw new Error('"publish.cloudflare.project" is required for kind "pages"');
|
|
97
|
+
const branch = cf.branch || 'main';
|
|
98
|
+
const directory = path.resolve(pkg.dirname, cf.directory || 'dist');
|
|
99
|
+
return { kind: 'pages', pkg, marker, project, branch, directory, label: `pages ${project} (${branch})` };
|
|
100
|
+
}
|
|
101
|
+
if (cf.kind === 'workers') {
|
|
102
|
+
const declared = cf.config;
|
|
103
|
+
const config = declared
|
|
104
|
+
? path.resolve(pkg.dirname, declared)
|
|
105
|
+
: this.workerConfigFiles.map(f => path.join(pkg.dirname, f)).find(f => fs.existsSync(f));
|
|
106
|
+
if (!config || !fs.existsSync(config)) {
|
|
107
|
+
throw new Error(declared
|
|
108
|
+
? `"publish.cloudflare.config" names ${declared}, which does not exist`
|
|
109
|
+
: `kind "workers" needs a wrangler configuration - none of ${this.workerConfigFiles.join(', ')} is in the package`);
|
|
110
|
+
}
|
|
111
|
+
return {
|
|
112
|
+
kind: 'workers',
|
|
113
|
+
pkg,
|
|
114
|
+
marker,
|
|
115
|
+
tag: `v${pkg.version}`,
|
|
116
|
+
config,
|
|
117
|
+
label: `workers ${path.relative(pkg.dirname, config)}`,
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
throw new Error(`"publish.cloudflare.kind" must be "pages" or "workers", not ${JSON.stringify(cf.kind)}`);
|
|
121
|
+
}
|
|
122
|
+
/** The API token and account id wrangler reads, or `undefined` when either is missing. */
|
|
123
|
+
credentials() {
|
|
124
|
+
const token = process.env.CLOUDFLARE_API_TOKEN;
|
|
125
|
+
const accountId = process.env.CLOUDFLARE_ACCOUNT_ID;
|
|
126
|
+
return token && accountId ? { token, accountId } : undefined;
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Whether this version is already deployed. A check that cannot be answered - the network, an
|
|
130
|
+
* expired token - reads as "not deployed", the catch-everything shape every target's registry
|
|
131
|
+
* check has: deploying a version again is harmless, failing the run over a question is not.
|
|
132
|
+
*/
|
|
133
|
+
async isDeployed(deploy) {
|
|
134
|
+
try {
|
|
135
|
+
if (deploy.kind === 'pages')
|
|
136
|
+
return await this.pagesHas(deploy);
|
|
137
|
+
return await this.workerHas(deploy);
|
|
138
|
+
}
|
|
139
|
+
catch {
|
|
140
|
+
return false;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
/** A Pages deployment of the project whose commit message is this version's marker. */
|
|
144
|
+
async pagesHas(deploy) {
|
|
145
|
+
const { token, accountId } = this.credentials();
|
|
146
|
+
const url = `https://api.cloudflare.com/client/v4/accounts/${accountId}/pages/projects/` +
|
|
147
|
+
`${encodeURIComponent(deploy.project)}/deployments`;
|
|
148
|
+
const res = await fetch(url, { headers: { Authorization: `Bearer ${token}` } });
|
|
149
|
+
if (!res.ok)
|
|
150
|
+
return false;
|
|
151
|
+
const body = (await res.json());
|
|
152
|
+
return (body.result ?? []).some(d => d.deployment_trigger?.metadata?.commit_message === deploy.marker);
|
|
153
|
+
}
|
|
154
|
+
/** A version of the Worker tagged with this version - among the ten most recent wrangler lists. */
|
|
155
|
+
async workerHas(deploy) {
|
|
156
|
+
const lines = [];
|
|
157
|
+
await this.wrangler(deploy.pkg, ['versions', 'list', '--json', '--config', deploy.config], (line, stream) => {
|
|
158
|
+
if (stream === 'stdout')
|
|
159
|
+
lines.push(line);
|
|
160
|
+
});
|
|
161
|
+
const versions = JSON.parse(lines.join('\n'));
|
|
162
|
+
return versions.some(v => v.annotations?.['workers/tag'] === deploy.tag);
|
|
163
|
+
}
|
|
164
|
+
/** Runs the deploy, its output shown on stderr as it comes and kept for the reason if it fails. */
|
|
165
|
+
async deploy(deploy) {
|
|
166
|
+
if (deploy.kind === 'pages' && !fs.existsSync(deploy.directory)) {
|
|
167
|
+
throw new Error(`${path.relative(deploy.pkg.dirname, deploy.directory)} does not exist - build the package first`);
|
|
168
|
+
}
|
|
169
|
+
const argv = deploy.kind === 'pages'
|
|
170
|
+
? [
|
|
171
|
+
'pages',
|
|
172
|
+
'deploy',
|
|
173
|
+
deploy.directory,
|
|
174
|
+
'--project-name',
|
|
175
|
+
deploy.project,
|
|
176
|
+
'--branch',
|
|
177
|
+
deploy.branch,
|
|
178
|
+
'--commit-message',
|
|
179
|
+
deploy.marker,
|
|
180
|
+
'--commit-dirty=true',
|
|
181
|
+
]
|
|
182
|
+
: ['deploy', '--config', deploy.config, '--tag', deploy.tag, '--message', deploy.marker];
|
|
183
|
+
const lines = [];
|
|
184
|
+
try {
|
|
185
|
+
await this.wrangler(deploy.pkg, argv, line => {
|
|
186
|
+
lines.push(line);
|
|
187
|
+
process.stderr.write(line + '\n');
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
catch (e) {
|
|
191
|
+
const tail = lines.filter(l => /error|✘/i.test(l)).slice(-4);
|
|
192
|
+
throw new Error(e.message + (tail.length ? ':\n' + tail.map(l => ` ${l.trim()}`).join('\n') : ''), {
|
|
193
|
+
cause: e,
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* `wrangler`, from the package's own or the repository's `node_modules` when it is installed there,
|
|
199
|
+
* and through `npx` when it is not - so a package that never added it as a dependency still
|
|
200
|
+
* deploys.
|
|
201
|
+
*/
|
|
202
|
+
async wrangler(pkg, argv, onLine) {
|
|
203
|
+
const options = { cwd: pkg.dirname, app: this.app, logLevel: 'silent', onLine };
|
|
204
|
+
try {
|
|
205
|
+
await runBin('wrangler', argv, options);
|
|
206
|
+
}
|
|
207
|
+
catch (e) {
|
|
208
|
+
if (!/was not found/.test(e?.message ?? ''))
|
|
209
|
+
throw e;
|
|
210
|
+
await runBin('npx', ['--yes', 'wrangler', ...argv], options);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { PublishTarget } from '../../../core/interfaces/publish-target.js';
|
|
2
|
+
import type { ConfigValue, ScopedVars } from '../../../interfaces/rman-config.interface.js';
|
|
3
|
+
/**
|
|
4
|
+
* **`publish.cloudflare.*`** - this target's own config block, declared beside it and reaching
|
|
5
|
+
* `RmanConfig` through the `PublishTargetConfigs` slot `publish.command.ts` exports, as
|
|
6
|
+
* `publish.docker.*` does.
|
|
7
|
+
*
|
|
8
|
+
* Required once `"cloudflare"` is one of a package's `publish.target`s.
|
|
9
|
+
*/
|
|
10
|
+
export interface CloudflarePublishOptions extends CloudflarePublishOptionsKeys, ScopedVars {
|
|
11
|
+
}
|
|
12
|
+
export interface CloudflarePublishOptionsKeys {
|
|
13
|
+
/**
|
|
14
|
+
* What the package deploys as: `"pages"` - a directory of static files uploaded to a Pages
|
|
15
|
+
* project (`wrangler pages deploy`) - or `"workers"` - a Worker, static assets included, as its
|
|
16
|
+
* own wrangler configuration describes it (`wrangler deploy`).
|
|
17
|
+
*/
|
|
18
|
+
kind: ConfigValue<'pages' | 'workers'>;
|
|
19
|
+
/** Pages: the project to deploy to. Required for `"pages"`. */
|
|
20
|
+
project?: ConfigValue<string>;
|
|
21
|
+
/** Pages: the branch the deployment is made for - the project's production branch puts it live.
|
|
22
|
+
* Default `"main"`. */
|
|
23
|
+
branch?: ConfigValue<string>;
|
|
24
|
+
/** Pages: the directory to upload, relative to the package. Default `"dist"`. */
|
|
25
|
+
directory?: ConfigValue<string>;
|
|
26
|
+
/** Workers: the wrangler configuration file, relative to the package. Default: the first of
|
|
27
|
+
* `wrangler.jsonc`, `wrangler.json`, `wrangler.toml` in the package. */
|
|
28
|
+
config?: ConfigValue<string>;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* **Cloudflare, as a publish target** - a static site on Pages, or a Worker with its assets.
|
|
32
|
+
*
|
|
33
|
+
* Opt-in like `docker`, so no `claims`: a package deploys here only by naming `"cloudflare"` in
|
|
34
|
+
* its own `publish.target`. What that buys over a workflow running `wrangler` on its own is what
|
|
35
|
+
* every target gets from `publish`: `publish.skip`, `--dry-run` and `--json`, and a deploy that
|
|
36
|
+
* happens once per version rather than once per merge.
|
|
37
|
+
*/
|
|
38
|
+
export declare const cloudflarePublishTarget: PublishTarget;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { CLOUDFLARE_TARGET } from './cloudflare-publish.service.js';
|
|
2
|
+
/**
|
|
3
|
+
* **Cloudflare, as a publish target** - a static site on Pages, or a Worker with its assets.
|
|
4
|
+
*
|
|
5
|
+
* Opt-in like `docker`, so no `claims`: a package deploys here only by naming `"cloudflare"` in
|
|
6
|
+
* its own `publish.target`. What that buys over a workflow running `wrangler` on its own is what
|
|
7
|
+
* every target gets from `publish`: `publish.skip`, `--dry-run` and `--json`, and a deploy that
|
|
8
|
+
* happens once per version rather than once per merge.
|
|
9
|
+
*/
|
|
10
|
+
/* **Last, with `docker`.** A site's build installs from the registries the other targets publish
|
|
11
|
+
* to, so it waits for what they published - the same reason `docker` runs last. */
|
|
12
|
+
export const cloudflarePublishTarget = {
|
|
13
|
+
name: CLOUDFLARE_TARGET,
|
|
14
|
+
describe: 'Deploy to Cloudflare Pages or Workers (wrangler)',
|
|
15
|
+
publishesLast: true,
|
|
16
|
+
getPlan(ctx) {
|
|
17
|
+
return ctx.app.getService('cloudflarePublish').getPlan(ctx.options);
|
|
18
|
+
},
|
|
19
|
+
applyPlan(ctx, plan) {
|
|
20
|
+
/** The entries are this target's own - `getPlan` produced them. */
|
|
21
|
+
return ctx.app.getService('cloudflarePublish').applyPlan(plan);
|
|
22
|
+
},
|
|
23
|
+
};
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { CloudflarePublishOptions } from '../builtins/publish-targets/cloudflare/cloudflare.target.js';
|
|
1
2
|
import type { DockerPublishOptions } from '../builtins/publish-targets/docker/docker.target.js';
|
|
2
3
|
import type { RmanApplication } from '../core/application.js';
|
|
3
4
|
import { type RmanConfig } from '../interfaces/rman-config.interface.js';
|
|
@@ -110,6 +111,9 @@ export interface PublishTargetConfigs {
|
|
|
110
111
|
/** Required once `"docker"` is one of a package's `publish.target`s - `publish --target docker`
|
|
111
112
|
* errors clearly on a package that opts in here but leaves this out. */
|
|
112
113
|
docker?: DockerPublishOptions;
|
|
114
|
+
/** Required once `"cloudflare"` is one of a package's `publish.target`s - `kind` says Pages or
|
|
115
|
+
* Workers. */
|
|
116
|
+
cloudflare?: CloudflarePublishOptions;
|
|
113
117
|
}
|
|
114
118
|
type Args = RmanConfig.ArgsOf<typeof config, typeof COMMAND>;
|
|
115
119
|
/**
|
package/constants.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const version = '2.
|
|
1
|
+
export const version = '2.23.0';
|
package/core/application.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { CloudflarePublishService, cloudflarePublishTarget } from '../builtins/publish-targets/cloudflare/index.js';
|
|
1
2
|
import { DockerPublishService, dockerPublishTarget } from '../builtins/publish-targets/docker/index.js';
|
|
2
3
|
import { ChangelogService } from '../services/changelog.service.js';
|
|
3
4
|
import { ConfigService } from '../services/config-service.js';
|
|
@@ -93,6 +94,7 @@ export class RmanApplication {
|
|
|
93
94
|
// Register core services.
|
|
94
95
|
this.setService('config', a => new ConfigService(a));
|
|
95
96
|
this.setService('changelog', a => new ChangelogService(a));
|
|
97
|
+
this.setService('cloudflarePublish', a => new CloudflarePublishService(a));
|
|
96
98
|
this.setService('dockerPublish', a => new DockerPublishService(a));
|
|
97
99
|
this.setService('githubRelease', a => new GithubReleaseService(a));
|
|
98
100
|
this.setService('exec', a => new ExecService(a));
|
|
@@ -102,6 +104,7 @@ export class RmanApplication {
|
|
|
102
104
|
this.setService('version', a => new VersionService(a));
|
|
103
105
|
// Register core targets.
|
|
104
106
|
this.publishTargets.add(dockerPublishTarget);
|
|
107
|
+
this.publishTargets.add(cloudflarePublishTarget);
|
|
105
108
|
}
|
|
106
109
|
/** Which platform claims a directory - the first whose manifest provider recognizes it, because
|
|
107
110
|
* before a package is read there is nothing else to go on. `basePlatform` when none does, so the
|