Try gh-workflow (#41155)

Experimenting with not writing YAML by hand...

Release Notes:

- N/A
This commit is contained in:
Conrad Irwin 2025-10-27 13:39:01 -06:00 committed by GitHub
parent db0f7a8b23
commit 58f07ff709
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 514 additions and 90 deletions

View file

@ -1,42 +1,37 @@
# generated `cargo xtask workflows`. Do not edit.
name: Danger
on:
pull_request:
branches: [main]
types:
- opened
- synchronize
- reopened
- edited
- opened
- synchronize
- reopened
- edited
branches:
- main
jobs:
danger:
if: github.repository_owner == 'zed-industries'
runs-on: namespace-profile-2x4-ubuntu-2404
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
- uses: pnpm/action-setup@fe02b34f77f8bc703788d5817da081398fad5dd2 # v4.0.0
with:
version: 9
- name: Setup Node
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
with:
node-version: "20"
cache: "pnpm"
cache-dependency-path: "script/danger/pnpm-lock.yaml"
- run: pnpm install --dir script/danger
- name: Run Danger
run: pnpm run --dir script/danger danger ci
env:
# This GitHub token is not used, but the value needs to be here to prevent
# Danger from throwing an error.
GITHUB_TOKEN: "not_a_real_token"
# All requests are instead proxied through an instance of
# https://github.com/maxdeviant/danger-proxy that allows Danger to securely
# authenticate with GitHub while still being able to run on PRs from forks.
DANGER_GITHUB_API_BASE_URL: "https://danger-proxy.fly.dev/github"
- name: steps::checkout_repo
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
- name: steps::setup_pnpm
uses: pnpm/action-setup@fe02b34f77f8bc703788d5817da081398fad5dd2
with:
version: '9'
- name: steps::danger::setup_node
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020
with:
node-version: '20'
cache: pnpm
cache-dependency-path: script/danger/pnpm-lock.yaml
- name: steps::danger::install_deps
run: pnpm install --dir script/danger
shell: bash -euxo pipefail {0}
- name: steps::danger::run
run: pnpm run --dir script/danger danger ci
shell: bash -euxo pipefail {0}
env:
GITHUB_TOKEN: not_a_real_token
DANGER_GITHUB_API_BASE_URL: https://danger-proxy.fly.dev/github

View file

@ -1,69 +1,76 @@
name: "Nix build"
# generated `cargo xtask workflows`. Do not edit.
name: Nix build
on:
workflow_call:
inputs:
flake-output:
type: string
default: "default"
default: default
cachix-filter:
type: string
default: ""
jobs:
nix-build:
timeout-minutes: 60
name: (${{ matrix.system.os }}) Nix Build
continue-on-error: true # TODO: remove when we want this to start blocking CI
strategy:
fail-fast: false
matrix:
system:
- os: x86 Linux
runner: namespace-profile-16x32-ubuntu-2204
install_nix: true
- os: arm Mac
runner: [macOS, ARM64, test]
install_nix: false
nix-build-linux-x86:
if: github.repository_owner == 'zed-industries'
runs-on: ${{ matrix.system.runner }}
runs-on: namespace-profile-16x32-ubuntu-2204
env:
ZED_CLIENT_CHECKSUM_SEED: ${{ secrets.ZED_CLIENT_CHECKSUM_SEED }}
ZED_MINIDUMP_ENDPOINT: ${{ secrets.ZED_SENTRY_MINIDUMP_ENDPOINT }}
ZED_MINIDUMP_ENDPOINT: ${{ secrets.ZED_MINIDUMP_ENDPOINT }}
ZED_CLOUD_PROVIDER_ADDITIONAL_MODELS_JSON: ${{ secrets.ZED_CLOUD_PROVIDER_ADDITIONAL_MODELS_JSON }}
GIT_LFS_SKIP_SMUDGE: 1 # breaks the livekit rust sdk examples which we don't actually depend on
GIT_LFS_SKIP_SMUDGE: '1'
steps:
- name: Checkout repo
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
with:
clean: false
# on our macs we manually install nix. for some reason the cachix action is running
# under a non-login /bin/bash shell which doesn't source the proper script to add the
# nix profile to PATH, so we manually add them here
- name: Set path
if: ${{ ! matrix.system.install_nix }}
run: |
echo "/nix/var/nix/profiles/default/bin" >> "$GITHUB_PATH"
echo "/Users/administrator/.nix-profile/bin" >> "$GITHUB_PATH"
- uses: cachix/install-nix-action@02a151ada4993995686f9ed4f1be7cfbb229e56f # v31
if: ${{ matrix.system.install_nix }}
with:
github_access_token: ${{ secrets.GITHUB_TOKEN }}
- uses: cachix/cachix-action@0fc020193b5a1fa3ac4575aa3a7d3aa6a35435ad # v16
with:
name: zed
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
pushFilter: "${{ inputs.cachix-filter }}"
cachixArgs: "-v"
- run: nix build .#${{ inputs.flake-output }} -L --accept-flake-config
- name: Limit /nix/store to 50GB on macs
if: ${{ ! matrix.system.install_nix }}
run: |
if [ "$(du -sm /nix/store | cut -f1)" -gt 50000 ]; then
- name: steps::checkout_repo
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
with:
clean: 'false'
- name: steps::nix::install_nix
uses: cachix/install-nix-action@02a151ada4993995686f9ed4f1be7cfbb229e56f
with:
github_access_token: ${{ secrets.GITHUB_TOKEN }}
- name: steps::nix::cachix_action
uses: cachix/cachix-action@0fc020193b5a1fa3ac4575aa3a7d3aa6a35435ad
with:
name: zed
authToken: ${{ secrets.CACHIX_AUTH_TOKEN }}
pushFilter: ${{ inputs.cachix-filter }}
cachixArgs: -v
- name: steps::nix::build
run: nix build .#${{ inputs.flake-output }} -L --accept-flake-config
shell: bash -euxo pipefail {0}
timeout-minutes: 60
continue-on-error: true
nix-build-mac-arm:
if: github.repository_owner == 'zed-industries'
runs-on: self-mini-macos
env:
ZED_CLIENT_CHECKSUM_SEED: ${{ secrets.ZED_CLIENT_CHECKSUM_SEED }}
ZED_MINIDUMP_ENDPOINT: ${{ secrets.ZED_MINIDUMP_ENDPOINT }}
ZED_CLOUD_PROVIDER_ADDITIONAL_MODELS_JSON: ${{ secrets.ZED_CLOUD_PROVIDER_ADDITIONAL_MODELS_JSON }}
GIT_LFS_SKIP_SMUDGE: '1'
steps:
- name: steps::checkout_repo
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
with:
clean: 'false'
- name: steps::nix::set_path
run: |
echo "/nix/var/nix/profiles/default/bin" >> "$GITHUB_PATH"
echo "/Users/administrator/.nix-profile/bin" >> "$GITHUB_PATH"
shell: bash -euxo pipefail {0}
- name: steps::nix::cachix_action
uses: cachix/cachix-action@0fc020193b5a1fa3ac4575aa3a7d3aa6a35435ad
with:
name: zed
authToken: ${{ secrets.CACHIX_AUTH_TOKEN }}
pushFilter: ${{ inputs.cachix-filter }}
cachixArgs: -v
- name: steps::nix::build
run: nix build .#${{ inputs.flake-output }} -L --accept-flake-config
shell: bash -euxo pipefail {0}
- name: steps::nix::limit_store
run: |-
if [ "$(du -sm /nix/store | cut -f1)" -gt 50000 ]; then
nix-collect-garbage -d || true
fi
fi
shell: bash -euxo pipefail {0}
timeout-minutes: 60
continue-on-error: true

107
Cargo.lock generated
View file

@ -4902,6 +4902,18 @@ dependencies = [
"syn 2.0.106",
]
[[package]]
name = "derive_setters"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae5c625eda104c228c06ecaf988d1c60e542176bd7a490e60eeda3493244c0c9"
dependencies = [
"darling 0.20.11",
"proc-macro2",
"quote",
"syn 2.0.106",
]
[[package]]
name = "deunicode"
version = "1.6.2"
@ -6942,6 +6954,33 @@ dependencies = [
"wasm-bindgen",
]
[[package]]
name = "gh-workflow"
version = "0.8.0"
source = "git+https://github.com/zed-industries/gh-workflow?rev=a0b197dd77c0ed2390c150e601f9d4f9a0ca7105#a0b197dd77c0ed2390c150e601f9d4f9a0ca7105"
dependencies = [
"async-trait",
"derive_more 2.0.1",
"derive_setters",
"gh-workflow-macros",
"indexmap 2.11.4",
"merge",
"serde",
"serde_json",
"serde_yaml",
"strum_macros 0.27.2",
]
[[package]]
name = "gh-workflow-macros"
version = "0.8.0"
source = "git+https://github.com/zed-industries/gh-workflow?rev=a0b197dd77c0ed2390c150e601f9d4f9a0ca7105#a0b197dd77c0ed2390c150e601f9d4f9a0ca7105"
dependencies = [
"heck 0.5.0",
"quote",
"syn 2.0.106",
]
[[package]]
name = "gif"
version = "0.13.3"
@ -9811,6 +9850,28 @@ dependencies = [
"gpui",
]
[[package]]
name = "merge"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "10bbef93abb1da61525bbc45eeaff6473a41907d19f8f9aa5168d214e10693e9"
dependencies = [
"merge_derive",
"num-traits",
]
[[package]]
name = "merge_derive"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "209d075476da2e63b4b29e72a2ef627b840589588e71400a25e3565c4f849d07"
dependencies = [
"proc-macro-error",
"proc-macro2",
"quote",
"syn 1.0.109",
]
[[package]]
name = "metal"
version = "0.29.0"
@ -12800,6 +12861,30 @@ dependencies = [
"toml_edit 0.23.7",
]
[[package]]
name = "proc-macro-error"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
dependencies = [
"proc-macro-error-attr",
"proc-macro2",
"quote",
"syn 1.0.109",
"version_check",
]
[[package]]
name = "proc-macro-error-attr"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
dependencies = [
"proc-macro2",
"quote",
"version_check",
]
[[package]]
name = "proc-macro-error-attr2"
version = "2.0.0"
@ -15224,6 +15309,19 @@ dependencies = [
"syn 2.0.106",
]
[[package]]
name = "serde_yaml"
version = "0.9.34+deprecated"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47"
dependencies = [
"indexmap 2.11.4",
"itoa",
"ryu",
"serde",
"unsafe-libyaml",
]
[[package]]
name = "serial2"
version = "0.2.33"
@ -18411,6 +18509,12 @@ version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7264e107f553ccae879d21fbea1d6724ac785e8c3bfc762137959b5802826ef3"
[[package]]
name = "unsafe-libyaml"
version = "0.2.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861"
[[package]]
name = "untrusted"
version = "0.9.0"
@ -20818,9 +20922,12 @@ name = "xtask"
version = "0.1.0"
dependencies = [
"anyhow",
"backtrace",
"cargo_metadata",
"cargo_toml",
"clap",
"gh-workflow",
"indexmap 2.11.4",
"indoc",
"toml 0.8.23",
"toml_edit 0.22.27",

View file

@ -508,6 +508,7 @@ fork = "0.2.0"
futures = "0.3"
futures-batch = "0.6.1"
futures-lite = "1.13"
gh-workflow = { git = "https://github.com/zed-industries/gh-workflow", rev = "a0b197dd77c0ed2390c150e601f9d4f9a0ca7105" }
git2 = { version = "0.20.1", default-features = false }
globset = "0.4"
handlebars = "4.3"

View file

@ -85,7 +85,7 @@ doctest = false
[dependencies]
anyhow.workspace = true
async-task = "4.7"
backtrace = { version = "0.3", optional = true }
backtrace = { workspace = true, optional = true }
blade-graphics = { workspace = true, optional = true }
blade-macros = { workspace = true, optional = true }
blade-util = { workspace = true, optional = true }
@ -234,7 +234,7 @@ windows-numerics = "0.2"
windows-registry = "0.5"
[dev-dependencies]
backtrace = "0.3"
backtrace.workspace = true
collections = { workspace = true, features = ["test-support"] }
env_logger.workspace = true
http_client = { workspace = true, features = ["test-support"] }

View file

@ -10,9 +10,12 @@ workspace = true
[dependencies]
anyhow.workspace = true
backtrace.workspace = true
cargo_metadata.workspace = true
cargo_toml.workspace = true
clap = { workspace = true, features = ["derive"] }
toml.workspace = true
indoc.workspace = true
indexmap.workspace = true
toml_edit.workspace = true
gh-workflow.workspace = true

View file

@ -20,6 +20,7 @@ enum CliCommand {
PackageConformity(tasks::package_conformity::PackageConformityArgs),
/// Publishes GPUI and its dependencies to crates.io.
PublishGpui(tasks::publish_gpui::PublishGpuiArgs),
Workflows(tasks::workflows::GenerateWorkflowArgs),
}
fn main() -> Result<()> {
@ -32,5 +33,6 @@ fn main() -> Result<()> {
tasks::package_conformity::run_package_conformity(args)
}
CliCommand::PublishGpui(args) => tasks::publish_gpui::run_publish_gpui(args),
CliCommand::Workflows(args) => tasks::workflows::run_workflows(args),
}
}

View file

@ -2,3 +2,4 @@ pub mod clippy;
pub mod licenses;
pub mod package_conformity;
pub mod publish_gpui;
pub mod workflows;

View file

@ -0,0 +1,33 @@
use anyhow::{Context, Result};
use clap::Parser;
use std::fs;
use std::path::Path;
mod runners;
mod steps;
mod vars;
mod workflows;
use workflows::*;
#[derive(Parser)]
pub struct GenerateWorkflowArgs {}
pub fn run_workflows(_: GenerateWorkflowArgs) -> Result<()> {
let dir = Path::new(".github/workflows");
let workflows = vec![("danger.yml", danger()), ("nix.yml", nix())];
fs::create_dir_all(dir)
.with_context(|| format!("Failed to create directory: {}", dir.display()))?;
for (filename, workflow) in workflows {
let content = workflow
.to_string()
.map_err(|e| anyhow::anyhow!("{}: {:?}", filename, e))?;
let content = format!("# generated `cargo xtask workflows`. Do not edit.\n{content}");
let file_path = dir.join(filename);
fs::write(&file_path, content)?;
}
Ok(())
}

View file

@ -0,0 +1,11 @@
pub const LINUX_CHEAP: Runner = Runner("namespace-profile-2x4-ubuntu-2404");
pub const LINUX_DEFAULT: Runner = Runner("namespace-profile-16x32-ubuntu-2204");
pub const MAC_DEFAULT: Runner = Runner("self-mini-macos");
pub struct Runner(&'static str);
impl Into<gh_workflow::RunsOn> for Runner {
fn into(self) -> gh_workflow::RunsOn {
self.0.into()
}
}

View file

@ -0,0 +1,142 @@
use gh_workflow::*;
pub fn checkout_repo() -> Step<Use> {
named::uses(
"actions",
"checkout",
"11bd71901bbe5b1630ceea73d27597364c9af683", // v4
)
}
pub fn setup_pnpm() -> Step<Use> {
named::uses(
"pnpm",
"action-setup",
"fe02b34f77f8bc703788d5817da081398fad5dd2", // v4.0.0
)
.add_with(("version", "9"))
}
pub mod danger {
use super::*;
pub fn setup_node() -> Step<Use> {
named::uses(
"actions",
"setup-node",
"49933ea5288caeca8642d1e84afbd3f7d6820020", // v4
)
.add_with(("node-version", "20"))
.add_with(("cache", "pnpm"))
.add_with(("cache-dependency-path", "script/danger/pnpm-lock.yaml"))
}
pub fn install_deps() -> Step<Run> {
named::run("pnpm install --dir script/danger")
}
pub fn run() -> Step<Run> {
named::run("pnpm run --dir script/danger danger ci")
// This GitHub token is not used, but the value needs to be here to prevent
// Danger from throwing an error.
.add_env(("GITHUB_TOKEN", "not_a_real_token"))
// All requests are instead proxied through an instance of
// https://github.com/maxdeviant/danger-proxy that allows Danger to securely
// authenticate with GitHub while still being able to run on PRs from forks.
.add_env((
"DANGER_GITHUB_API_BASE_URL",
"https://danger-proxy.fly.dev/github",
))
}
}
pub mod nix {
use indoc::indoc;
use crate::tasks::workflows::vars;
use super::*;
// on our macs we manually install nix. for some reason the cachix action is running
// under a non-login /bin/bash shell which doesn't source the proper script to add the
// nix profile to PATH, so we manually add them here
pub fn set_path() -> Step<Run> {
named::run(indoc! {r#"
echo "/nix/var/nix/profiles/default/bin" >> "$GITHUB_PATH"
echo "/Users/administrator/.nix-profile/bin" >> "$GITHUB_PATH"
"#})
}
pub fn install_nix() -> Step<Use> {
named::uses(
"cachix",
"install-nix-action",
"02a151ada4993995686f9ed4f1be7cfbb229e56f", // v31
)
.add_with(("github_access_token", vars::GITHUB_TOKEN))
}
pub fn cachix_action(cachix_filter: &str) -> Step<Use> {
named::uses(
"cachix",
"cachix-action",
"0fc020193b5a1fa3ac4575aa3a7d3aa6a35435ad", // v16
)
.add_with(("name", "zed"))
.add_with(("authToken", vars::CACHIX_AUTH_TOKEN))
.add_with(("pushFilter", cachix_filter))
.add_with(("cachixArgs", "-v"))
}
pub fn build(flake_output: &str) -> Step<Run> {
named::run(&format!(
"nix build .#{} -L --accept-flake-config",
flake_output
))
}
pub fn limit_store() -> Step<Run> {
named::run(indoc! {r#"
if [ "$(du -sm /nix/store | cut -f1)" -gt 50000 ]; then
nix-collect-garbage -d || true
fi"#
})
}
}
// (janky) helpers to generate steps with a name that corresponds
// to the name of the calling function.
mod named {
use gh_workflow::*;
pub(super) fn uses(owner: &str, repo: &str, ref_: &str) -> Step<Use> {
Step::new(function_name(1)).uses(owner, repo, ref_)
}
pub(super) fn run(script: &str) -> Step<Run> {
Step::new(function_name(1))
.run(script)
.shell("bash -euxo pipefail {0}")
}
fn function_name(i: usize) -> String {
let mut name = "<unknown>".to_string();
let mut count = 0;
backtrace::trace(|frame| {
if count < i + 3 {
count += 1;
return true;
}
backtrace::resolve_frame(frame, |cb| {
if let Some(s) = cb.name() {
name = s.to_string()
}
});
false
});
name.split("::")
.skip_while(|s| s != &"steps")
.collect::<Vec<_>>()
.join("::")
}
}

View file

@ -0,0 +1,17 @@
use gh_workflow::WorkflowCallInput;
macro_rules! secret {
($secret_name:ident) => {
pub const $secret_name: &str = concat!("${{ secrets.", stringify!($secret_name), " }}");
};
}
secret!(GITHUB_TOKEN);
secret!(CACHIX_AUTH_TOKEN);
secret!(ZED_CLIENT_CHECKSUM_SEED);
secret!(ZED_MINIDUMP_ENDPOINT);
secret!(ZED_CLOUD_PROVIDER_ADDITIONAL_MODELS_JSON);
pub fn input(name: &str, input: WorkflowCallInput) -> (String, (&str, WorkflowCallInput)) {
return (format!("${{{{ inputs.{name} }}}}"), (name, input));
}

View file

@ -0,0 +1,105 @@
use gh_workflow::*;
use indexmap::IndexMap;
use super::{runners, steps, vars};
/// Generates the danger.yml workflow
pub fn danger() -> Workflow {
Workflow::default()
.name("Danger")
.on(
Event::default().pull_request(PullRequest::default().add_branch("main").types([
PullRequestType::Opened,
PullRequestType::Synchronize,
PullRequestType::Reopened,
PullRequestType::Edited,
])),
)
.add_job(
"danger",
Job::default()
.cond(Expression::new(
"github.repository_owner == 'zed-industries'",
))
.runs_on(runners::LINUX_CHEAP)
.add_step(steps::checkout_repo())
.add_step(steps::setup_pnpm())
.add_step(steps::danger::setup_node())
.add_step(steps::danger::install_deps())
.add_step(steps::danger::run()),
)
}
/// Generates the nix.yml workflow
pub fn nix() -> Workflow {
let env: IndexMap<_, _> = [
("ZED_CLIENT_CHECKSUM_SEED", vars::ZED_CLIENT_CHECKSUM_SEED),
("ZED_MINIDUMP_ENDPOINT", vars::ZED_MINIDUMP_ENDPOINT),
(
"ZED_CLOUD_PROVIDER_ADDITIONAL_MODELS_JSON",
vars::ZED_CLOUD_PROVIDER_ADDITIONAL_MODELS_JSON,
),
("GIT_LFS_SKIP_SMUDGE", "1"), // breaks the livekit rust sdk examples which we don't actually depend on
]
.into_iter()
.map(|(key, value)| (key.into(), value.into()))
.collect();
// todo(ci) instead of having these as optional YAML inputs,
// should we just generate two copies of the job (one for release-nightly
// and one for CI?)
let (input_flake_output, flake_output) = vars::input(
"flake-output",
WorkflowCallInput {
input_type: "string".into(),
default: Some("default".into()),
..Default::default()
},
);
let (input_cachix_filter, cachix_filter) = vars::input(
"cachix-filter",
WorkflowCallInput {
input_type: "string".into(),
..Default::default()
},
);
Workflow::default()
.name("Nix build")
.on(Event::default().workflow_call(
WorkflowCall::default()
.add_input(flake_output.0, flake_output.1)
.add_input(cachix_filter.0, cachix_filter.1),
))
.add_job(
"nix-build-linux-x86",
Job::default()
.timeout_minutes(60u32)
.continue_on_error(true)
.cond(Expression::new(
"github.repository_owner == 'zed-industries'",
))
.runs_on(runners::LINUX_DEFAULT)
.env(env.clone())
.add_step(steps::checkout_repo().add_with(("clean", "false")))
.add_step(steps::nix::install_nix())
.add_step(steps::nix::cachix_action(&input_cachix_filter))
.add_step(steps::nix::build(&input_flake_output)),
)
.add_job(
"nix-build-mac-arm",
Job::default()
.timeout_minutes(60u32)
.continue_on_error(true)
.cond(Expression::new(
"github.repository_owner == 'zed-industries'",
))
.runs_on(runners::MAC_DEFAULT)
.env(env)
.add_step(steps::checkout_repo().add_with(("clean", "false")))
.add_step(steps::nix::set_path())
.add_step(steps::nix::cachix_action(&input_cachix_filter))
.add_step(steps::nix::build(&input_flake_output))
.add_step(steps::nix::limit_store()),
)
}