Skip to repository content

tenant.openagents/omega

No repository description is available.

OpenAgents Git authority 2026-07-28T05:38:22.188Z Public web read
NIP-34 coordinate30617:7649603503856e5148d571eac2766b288a8ff1e9e35d380337a1d2b0015b4f92:omega
MaintainersHidden in public view
References2 branches · 1 tag
Read-only clonegit clone https://openagents.com/git/tenant.openagents/omega.git
Browse files

deploy_docs.rs

374 lines · 13.0 KB · rust
1use gh_workflow::{
2    Event, Expression, Input, Job, Level, Permissions, Push, Run, Step, Use, UsesJob, Workflow,
3    WorkflowCall, WorkflowCallSecret, WorkflowDispatch,
4};
5
6use crate::tasks::workflows::{
7    runners,
8    steps::{
9        self, CommonJobConditions, CommonPermissionSets, FluentBuilder as _, NamedJob,
10        UploadArtifactStep, named, release_job,
11    },
12    vars::{self, StepOutput, WorkflowInput},
13};
14
15const BUILD_OUTPUT_DIR: &str = "target/deploy";
16
17pub(crate) enum DocsChannel {
18    Nightly,
19    Preview,
20    Stable,
21}
22
23impl DocsChannel {
24    pub(crate) fn site_url(&self) -> &'static str {
25        match self {
26            Self::Nightly => "/docs/nightly/",
27            Self::Preview => "/docs/preview/",
28            Self::Stable => "/docs/",
29        }
30    }
31
32    pub(crate) fn project_name(&self) -> &'static str {
33        match self {
34            Self::Nightly => "docs-nightly",
35            Self::Preview => "docs-preview",
36            Self::Stable => "docs",
37        }
38    }
39
40    pub(crate) fn channel_name(&self) -> &'static str {
41        match self {
42            Self::Nightly => "nightly",
43            Self::Preview => "preview",
44            Self::Stable => "stable",
45        }
46    }
47}
48
49pub(crate) fn lychee_link_check(dir: &str) -> Step<Use> {
50    named::uses(
51        "lycheeverse",
52        "lychee-action",
53        "82202e5e9c2f4ef1a55a3d02563e1cb6041e5332",
54    ) // v2.4.1
55    .add_with(("args", format!("--no-progress --exclude '^http' '{dir}'")))
56    .add_with(("fail", true))
57    .add_with(("jobSummary", false))
58}
59
60pub(crate) fn install_mdbook() -> Step<Use> {
61    named::uses(
62        "peaceiris",
63        "actions-mdbook",
64        "ee69d230fe19748b7abf22df32acaa93833fad08", // v2
65    )
66    .with(("mdbook-version", "0.4.37"))
67}
68
69pub(crate) fn build_docs_book(docs_channel: String, site_url: String) -> Step<Run> {
70    named::bash(indoc::formatdoc! {r#"
71        mkdir -p {BUILD_OUTPUT_DIR}
72        mdbook build ./docs --dest-dir=../{BUILD_OUTPUT_DIR}/docs/
73    "#})
74    .add_env(("DOCS_CHANNEL", docs_channel))
75    .add_env(("MDBOOK_BOOK__SITE_URL", site_url))
76}
77
78fn docs_build_steps(
79    job: Job,
80    checkout_ref: Option<String>,
81    docs_channel: impl Into<String>,
82    site_url: impl Into<String>,
83) -> Job {
84    let docs_channel = docs_channel.into();
85    let site_url = site_url.into();
86
87    steps::use_clang(
88        job.add_env(("DOCS_AMPLITUDE_API_KEY", vars::DOCS_AMPLITUDE_API_KEY))
89            .add_env(("DOCS_CONSENT_IO_INSTANCE", vars::DOCS_CONSENT_IO_INSTANCE))
90            .add_step(
91                steps::checkout_repo().when_some(checkout_ref, |step, checkout_ref| {
92                    step.with_ref(checkout_ref)
93                }),
94            )
95            .runs_on(runners::LINUX_XL)
96            .add_step(steps::setup_cargo_config(runners::Platform::Linux))
97            .add_step(steps::cache_rust_dependencies_namespace())
98            .map(steps::install_linux_dependencies)
99            .add_step(steps::script("./script/generate-action-metadata"))
100            .add_step(lychee_link_check("./docs/src/**/*"))
101            .add_step(install_mdbook())
102            .add_step(build_docs_book(docs_channel, site_url))
103            .add_step(lychee_link_check(&format!("{BUILD_OUTPUT_DIR}/docs"))),
104    )
105}
106
107fn docs_deploy_steps(job: Job, project_name: &StepOutput) -> Job {
108    fn deploy_to_cf_pages(project_name: &StepOutput) -> Step<Use> {
109        named::uses(
110            "cloudflare",
111            "wrangler-action",
112            "da0e0dfe58b7a431659754fdf3f186c529afbe65",
113        ) // v3
114        .add_with(("apiToken", vars::CLOUDFLARE_API_TOKEN))
115        .add_with(("accountId", vars::CLOUDFLARE_ACCOUNT_ID))
116        .add_with((
117            "command",
118            format!(
119                "pages deploy {BUILD_OUTPUT_DIR} --project-name=${{{{ {} }}}} --branch main",
120                project_name.expr()
121            ),
122        ))
123    }
124
125    fn upload_install_script() -> Step<Use> {
126        named::uses(
127            "cloudflare",
128            "wrangler-action",
129            "da0e0dfe58b7a431659754fdf3f186c529afbe65",
130        ) // v3
131        .add_with(("apiToken", vars::CLOUDFLARE_API_TOKEN))
132        .add_with(("accountId", vars::CLOUDFLARE_ACCOUNT_ID))
133        .add_with((
134            "command",
135            "r2 object put -f script/install.sh zed-open-source-website-assets/install.sh",
136        ))
137    }
138
139    fn deploy_docs_worker() -> Step<Use> {
140        named::uses(
141            "cloudflare",
142            "wrangler-action",
143            "da0e0dfe58b7a431659754fdf3f186c529afbe65",
144        ) // v3
145        .add_with(("apiToken", vars::CLOUDFLARE_API_TOKEN))
146        .add_with(("accountId", vars::CLOUDFLARE_ACCOUNT_ID))
147        .add_with(("command", "deploy .cloudflare/docs-proxy/src/worker.js"))
148    }
149
150    fn upload_wrangler_logs() -> UploadArtifactStep {
151        steps::upload_artifact("wrangler_logs", "/home/runner/.config/.wrangler/logs/")
152            .if_condition(Expression::new("always()"))
153    }
154
155    job.add_step(deploy_to_cf_pages(project_name))
156        .add_step(upload_install_script())
157        .add_step(deploy_docs_worker())
158        .add_step(upload_wrangler_logs())
159}
160
161pub(crate) fn check_docs() -> NamedJob {
162    NamedJob {
163        name: "check_docs".to_owned(),
164        job: docs_build_steps(
165            release_job(&[]).add_step(steps::harden_runner()),
166            None,
167            DocsChannel::Stable.channel_name(),
168            DocsChannel::Stable.site_url(),
169        ),
170    }
171}
172
173fn resolve_channel_step(
174    channel_expr: impl Into<String>,
175) -> (Step<Run>, StepOutput, StepOutput, StepOutput) {
176    let step = Step::new("deploy_docs::resolve_channel_step").run(format!(
177        indoc::indoc! {r#"
178            if [ -z "$CHANNEL" ]; then
179                if [ "$GITHUB_REF" = "refs/heads/main" ]; then
180                    CHANNEL="nightly"
181                else
182                    echo "::error::channel input is required when ref is not main."
183                    exit 1
184                fi
185            fi
186
187            case "$CHANNEL" in
188                "nightly")
189                    SITE_URL="{nightly_site_url}"
190                    PROJECT_NAME="{nightly_project_name}"
191                    ;;
192                "preview")
193                    SITE_URL="{preview_site_url}"
194                    PROJECT_NAME="{preview_project_name}"
195                    ;;
196                "stable")
197                    SITE_URL="{stable_site_url}"
198                    PROJECT_NAME="{stable_project_name}"
199                    ;;
200                *)
201                    echo "::error::Invalid docs channel '$CHANNEL'. Expected one of: nightly, preview, stable."
202                    exit 1
203                    ;;
204            esac
205
206            {{
207                echo "channel=$CHANNEL"
208                echo "site_url=$SITE_URL"
209                echo "project_name=$PROJECT_NAME"
210            }} >> "$GITHUB_OUTPUT"
211        "#},
212        nightly_site_url = DocsChannel::Nightly.site_url(),
213        preview_site_url = DocsChannel::Preview.site_url(),
214        stable_site_url = DocsChannel::Stable.site_url(),
215        nightly_project_name = DocsChannel::Nightly.project_name(),
216        preview_project_name = DocsChannel::Preview.project_name(),
217        stable_project_name = DocsChannel::Stable.project_name(),
218    ))
219    .id("resolve-channel")
220    .add_env(("CHANNEL", channel_expr.into()));
221
222    let channel = StepOutput::new(&step, "channel");
223    let site_url = StepOutput::new(&step, "site_url");
224    let project_name = StepOutput::new(&step, "project_name");
225    (step, channel, site_url, project_name)
226}
227
228fn docs_job(channel_expr: impl Into<String>, checkout_ref: Option<String>) -> NamedJob {
229    let (resolve_step, channel, site_url, project_name) = resolve_channel_step(channel_expr);
230
231    NamedJob {
232        name: "deploy_docs".to_owned(),
233        job: docs_deploy_steps(
234            docs_build_steps(
235                release_job(&[])
236                    .cond(Expression::new(
237                        "github.repository_owner == 'zed-industries'",
238                    ))
239                    .name("Build and Deploy Docs")
240                    .add_step(resolve_step),
241                checkout_ref,
242                channel.to_string(),
243                site_url.to_string(),
244            ),
245            &project_name,
246        ),
247    }
248}
249
250pub(crate) fn deploy_docs_workflow_call(
251    channel: impl Into<String>,
252    checkout_ref: impl Into<String>,
253) -> NamedJob<UsesJob> {
254    let job = Job::default()
255        .with_repository_owner_guard()
256        .permissions(Permissions::default().contents(Level::Read))
257        .uses(
258            "zed-industries",
259            "zed",
260            ".github/workflows/deploy_docs.yml",
261            // Pinned to a commit rather than the mutable `main` ref (supply-chain hardening).
262            // Same-repo reusable workflow; bump via Dependabot or alongside deploy_docs.yml changes.
263            "3f16f7b9082f8828e4d6ae207d2349b1ef932517",
264        )
265        .with(
266            Input::default()
267                .add("channel", channel.into())
268                .add("checkout_ref", checkout_ref.into()),
269        )
270        .secrets(indexmap::IndexMap::from([
271            (
272                "DOCS_AMPLITUDE_API_KEY".to_owned(),
273                vars::DOCS_AMPLITUDE_API_KEY.to_owned(),
274            ),
275            (
276                "DOCS_CONSENT_IO_INSTANCE".to_owned(),
277                vars::DOCS_CONSENT_IO_INSTANCE.to_owned(),
278            ),
279            (
280                "CLOUDFLARE_API_TOKEN".to_owned(),
281                vars::CLOUDFLARE_API_TOKEN.to_owned(),
282            ),
283            (
284                "CLOUDFLARE_ACCOUNT_ID".to_owned(),
285                vars::CLOUDFLARE_ACCOUNT_ID.to_owned(),
286            ),
287        ]));
288
289    NamedJob {
290        name: "deploy_docs".to_owned(),
291        job,
292    }
293}
294
295pub(crate) fn deploy_docs_job(
296    channel_input: &WorkflowInput,
297    checkout_ref_input: &WorkflowInput,
298) -> NamedJob {
299    docs_job(
300        channel_input.to_string(),
301        Some(format!(
302            "${{{{ {} != '' && {} || github.sha }}}}",
303            checkout_ref_input.expr(),
304            checkout_ref_input.expr()
305        )),
306    )
307}
308
309pub(crate) fn deploy_docs() -> Workflow {
310    let channel = WorkflowInput::string("channel", Some(String::new()))
311        .description("Docs channel to deploy: nightly, preview, or stable");
312    let checkout_ref = WorkflowInput::string("checkout_ref", Some(String::new()))
313        .description("Git ref to checkout and deploy. Defaults to event SHA when omitted.");
314    let deploy_docs = deploy_docs_job(&channel, &checkout_ref);
315
316    named::workflow()
317        .with_minimal_permissions()
318        .add_event(
319            Event::default().workflow_dispatch(
320                WorkflowDispatch::default()
321                    .add_input(channel.name, channel.input())
322                    .add_input(checkout_ref.name, checkout_ref.input()),
323            ),
324        )
325        .add_event(
326            Event::default().workflow_call(
327                WorkflowCall::default()
328                    .add_input(channel.name, channel.call_input())
329                    .add_input(checkout_ref.name, checkout_ref.call_input())
330                    .secrets([
331                        (
332                            "DOCS_AMPLITUDE_API_KEY".to_owned(),
333                            WorkflowCallSecret {
334                                description: "DOCS_AMPLITUDE_API_KEY".to_owned(),
335                                required: true,
336                            },
337                        ),
338                        (
339                            "DOCS_CONSENT_IO_INSTANCE".to_owned(),
340                            WorkflowCallSecret {
341                                description: "DOCS_CONSENT_IO_INSTANCE".to_owned(),
342                                required: true,
343                            },
344                        ),
345                        (
346                            "CLOUDFLARE_API_TOKEN".to_owned(),
347                            WorkflowCallSecret {
348                                description: "CLOUDFLARE_API_TOKEN".to_owned(),
349                                required: true,
350                            },
351                        ),
352                        (
353                            "CLOUDFLARE_ACCOUNT_ID".to_owned(),
354                            WorkflowCallSecret {
355                                description: "CLOUDFLARE_ACCOUNT_ID".to_owned(),
356                                required: true,
357                            },
358                        ),
359                    ]),
360            ),
361        )
362        .add_job(deploy_docs.name, deploy_docs.job)
363}
364
365pub(crate) fn deploy_nightly_docs() -> Workflow {
366    let deploy_docs = deploy_docs_workflow_call("nightly", "${{ github.sha }}");
367
368    named::workflow()
369        .name("deploy_nightly_docs")
370        .permissions(Permissions::default())
371        .add_event(Event::default().push(Push::default().add_branch("main")))
372        .add_job(deploy_docs.name, deploy_docs.job)
373}
374
Served at tenant.openagents/omega Member data and write actions are omitted.