diff --git a/.github/workflows/extension_bump.yml b/.github/workflows/extension_bump.yml new file mode 100644 index 0000000000000000000000000000000000000000..5933ab7fbb2fab753cbda729c82026102e395539 --- /dev/null +++ b/.github/workflows/extension_bump.yml @@ -0,0 +1,136 @@ +# Generated from xtask::workflows::extension_bump +# Rebuild with `cargo xtask workflows`. +name: extension_bump +env: + CARGO_TERM_COLOR: always + RUST_BACKTRACE: '1' + CARGO_INCREMENTAL: '0' + ZED_EXTENSION_CLI_SHA: 7cfce605704d41ca247e3f84804bf323f6c6caaf +on: + workflow_call: + inputs: + bump-type: + description: bump-type + type: string + default: patch + secrets: + app-id: + description: The app ID used to create the PR + required: true + app-secret: + description: The app secret for the corresponding app ID + required: true +jobs: + check_extension: + if: (github.repository_owner == 'zed-industries' || github.repository_owner == 'zed-extensions') + runs-on: namespace-profile-2x4-ubuntu-2404 + steps: + - name: steps::checkout_repo + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 + with: + clean: false + - id: cache-zed-extension-cli + name: extension_tests::cache_zed_extension_cli + uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 + with: + path: zed-extension + key: zed-extension-${{ env.ZED_EXTENSION_CLI_SHA }} + - name: extension_tests::download_zed_extension_cli + if: steps.cache-zed-extension-cli.outputs.cache-hit != 'true' + run: | + wget --quiet "https://zed-extension-cli.nyc3.digitaloceanspaces.com/$ZED_EXTENSION_CLI_SHA/x86_64-unknown-linux-gnu/zed-extension" + chmod +x zed-extension + shell: bash -euxo pipefail {0} + - name: extension_tests::check + run: | + mkdir -p /tmp/ext-scratch + mkdir -p /tmp/ext-output + ./zed-extension --source-dir . --scratch-dir /tmp/ext-scratch --output-dir /tmp/ext-output + shell: bash -euxo pipefail {0} + timeout-minutes: 1 + check_bump_needed: + if: (github.repository_owner == 'zed-industries' || github.repository_owner == 'zed-extensions') + runs-on: namespace-profile-2x4-ubuntu-2404 + steps: + - name: steps::checkout_repo + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 + with: + clean: false + fetch-depth: 10 + - id: compare-versions-check + name: extension_bump::compare_versions + run: |+ + CURRENT_VERSION="$(sed -n 's/version = \"\(.*\)\"/\1/p' < extension.toml)" + + git checkout "$(git log -1 --format=%H)"~1 + + PREV_COMMIT_VERSION="$(sed -n 's/version = \"\(.*\)\"/\1/p' < extension.toml)" + + [[ "$CURRENT_VERSION" == "$PREV_COMMIT_VERSION" ]] && \ + echo "needs_bump=true" >> "$GITHUB_OUTPUT" || \ + echo "needs_bump=false" >> "$GITHUB_OUTPUT" + + shell: bash -euxo pipefail {0} + outputs: + needs_bump: ${{ steps.compare-versions-check.outputs.needs_bump }} + timeout-minutes: 1 + bump_extension_version: + needs: + - check_extension + - check_bump_needed + if: (github.repository_owner == 'zed-industries' || github.repository_owner == 'zed-extensions') && needs.check_bump_needed.outputs.needs_bump == 'true' + runs-on: namespace-profile-8x16-ubuntu-2204 + steps: + - id: generate-token + name: extension_bump::generate_token + uses: actions/create-github-app-token@v2 + with: + app-id: ${{ secrets.app-id }} + private-key: ${{ secrets.app-secret }} + - name: steps::checkout_repo + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 + with: + clean: false + - name: extension_bump::install_bump_2_version + run: pip install bump2version + shell: bash -euxo pipefail {0} + - id: bump-version + name: extension_bump::bump_version + run: | + OLD_VERSION="$(sed -n 's/version = \"\(.*\)\"/\1/p' < extension.toml)" + + cat < .bumpversion.cfg + [bumpversion] + current_version = "$OLD_VERSION" + + [bumpversion:file:Cargo.toml] + + [bumpversion:file:extension.toml] + + EOF + + bump2version --verbose ${{ inputs.bump-type }} + NEW_VERSION="$(sed -n 's/version = \"\(.*\)\"/\1/p' < extension.toml)" + cargo update --workspace + + rm .bumpversion.cfg + + echo "old_version=${OLD_VERSION}" >> "$GITHUB_OUTPUT" + echo "new_version=${NEW_VERSION}" >> "$GITHUB_OUTPUT" + shell: bash -euxo pipefail {0} + - name: extension_bump::create_pull_request + uses: peter-evans/create-pull-request@v7 + with: + title: Bump version to ${{ steps.bump-version.outputs.new_version }} + body: This PR bumps the version of this extension to v${{ steps.bump-version.outputs.new_version }} + commit-message: Bump version to v${{ steps.bump-version.outputs.new_version }} + branch: bump-from-${{ steps.bump-version.outputs.old_version }} + committer: zed-zippy[bot] <234243425+zed-zippy[bot]@users.noreply.github.com> + base: main + delete-branch: true + token: ${{ steps.generate-token.outputs.token }} + sign-commits: true + timeout-minutes: 1 +concurrency: + group: ${{ github.workflow }}-${{ github.ref_name }}-${{ github.ref_name == 'main' && github.sha || 'anysha' }} + cancel-in-progress: true diff --git a/tooling/xtask/src/tasks/workflows.rs b/tooling/xtask/src/tasks/workflows.rs index c18eca52be8cf7fa369f46427c58b1d6b70e8bd0..334bc71e46fc5d7d99a9fab238dc60b874a1093d 100644 --- a/tooling/xtask/src/tasks/workflows.rs +++ b/tooling/xtask/src/tasks/workflows.rs @@ -7,6 +7,7 @@ mod after_release; mod cherry_pick; mod compare_perf; mod danger; +mod extension_bump; mod extension_tests; mod nix_build; mod release_nightly; @@ -44,6 +45,7 @@ pub fn run_workflows(_: GenerateWorkflowArgs) -> Result<()> { ("run_agent_evals.yml", run_agent_evals::run_agent_evals()), ("after_release.yml", after_release::after_release()), ("extension_tests.yml", extension_tests::extension_tests()), + ("extension_bump.yml", extension_bump::extension_bump()), ]; fs::create_dir_all(dir) .with_context(|| format!("Failed to create directory: {}", dir.display()))?; diff --git a/tooling/xtask/src/tasks/workflows/cherry_pick.rs b/tooling/xtask/src/tasks/workflows/cherry_pick.rs index 6181d79e042365b87ce2d6ad00a75580c71344a2..105bf74c4194a46ad4ca62991fae3a945eea150d 100644 --- a/tooling/xtask/src/tasks/workflows/cherry_pick.rs +++ b/tooling/xtask/src/tasks/workflows/cherry_pick.rs @@ -3,14 +3,14 @@ use gh_workflow::*; use crate::tasks::workflows::{ runners, steps::{self, NamedJob, named}, - vars::{self, Input, StepOutput}, + vars::{self, StepOutput, WorkflowInput}, }; pub fn cherry_pick() -> Workflow { - let branch = Input::string("branch", None); - let commit = Input::string("commit", None); - let channel = Input::string("channel", None); - let pr_number = Input::string("pr_number", None); + let branch = WorkflowInput::string("branch", None); + let commit = WorkflowInput::string("commit", None); + let channel = WorkflowInput::string("channel", None); + let pr_number = WorkflowInput::string("pr_number", None); let cherry_pick = run_cherry_pick(&branch, &commit, &channel); named::workflow() .run_name(format!("cherry_pick to {channel} #{pr_number}")) @@ -24,7 +24,11 @@ pub fn cherry_pick() -> Workflow { .add_job(cherry_pick.name, cherry_pick.job) } -fn run_cherry_pick(branch: &Input, commit: &Input, channel: &Input) -> NamedJob { +fn run_cherry_pick( + branch: &WorkflowInput, + commit: &WorkflowInput, + channel: &WorkflowInput, +) -> NamedJob { fn authenticate_as_zippy() -> (Step, StepOutput) { let step = named::uses( "actions", @@ -39,9 +43,9 @@ fn run_cherry_pick(branch: &Input, commit: &Input, channel: &Input) -> NamedJob } fn cherry_pick( - branch: &Input, - commit: &Input, - channel: &Input, + branch: &WorkflowInput, + commit: &WorkflowInput, + channel: &WorkflowInput, token: &StepOutput, ) -> Step { named::bash(&format!("./script/cherry-pick {branch} {commit} {channel}")) diff --git a/tooling/xtask/src/tasks/workflows/compare_perf.rs b/tooling/xtask/src/tasks/workflows/compare_perf.rs index db9f21de15fe159c369ad603e3ab0ff93e1cc7f9..1d111acc4f8a4dc47edea6f45c0b93c845b7cda2 100644 --- a/tooling/xtask/src/tasks/workflows/compare_perf.rs +++ b/tooling/xtask/src/tasks/workflows/compare_perf.rs @@ -5,13 +5,13 @@ use crate::tasks::workflows::steps::FluentBuilder; use crate::tasks::workflows::{ runners, steps::{self, NamedJob, named}, - vars::Input, + vars::WorkflowInput, }; pub fn compare_perf() -> Workflow { - let head = Input::string("head", None); - let base = Input::string("base", None); - let crate_name = Input::string("crate_name", Some("".to_owned())); + let head = WorkflowInput::string("head", None); + let base = WorkflowInput::string("base", None); + let crate_name = WorkflowInput::string("crate_name", Some("".to_owned())); let run_perf = run_perf(&base, &head, &crate_name); named::workflow() .on(Event::default().workflow_dispatch( @@ -23,8 +23,12 @@ pub fn compare_perf() -> Workflow { .add_job(run_perf.name, run_perf.job) } -pub fn run_perf(base: &Input, head: &Input, crate_name: &Input) -> NamedJob { - fn cargo_perf_test(ref_name: &Input, crate_name: &Input) -> Step { +pub fn run_perf( + base: &WorkflowInput, + head: &WorkflowInput, + crate_name: &WorkflowInput, +) -> NamedJob { + fn cargo_perf_test(ref_name: &WorkflowInput, crate_name: &WorkflowInput) -> Step { named::bash(&format!( " if [ -n \"{crate_name}\" ]; then @@ -39,7 +43,7 @@ pub fn run_perf(base: &Input, head: &Input, crate_name: &Input) -> NamedJob { named::uses("taiki-e", "install-action", "hyperfine") } - fn compare_runs(head: &Input, base: &Input) -> Step { + fn compare_runs(head: &WorkflowInput, base: &WorkflowInput) -> Step { named::bash(&format!( "cargo perf-compare --save=results.md {base} {head}" )) diff --git a/tooling/xtask/src/tasks/workflows/extension_bump.rs b/tooling/xtask/src/tasks/workflows/extension_bump.rs new file mode 100644 index 0000000000000000000000000000000000000000..66de1f86aa998269abc24f1de375dbe1800acc31 --- /dev/null +++ b/tooling/xtask/src/tasks/workflows/extension_bump.rs @@ -0,0 +1,217 @@ +use gh_workflow::*; +use indoc::indoc; + +use crate::tasks::workflows::{ + extension_tests::{self}, + runners, + steps::{self, CommonJobConditions, DEFAULT_REPOSITORY_OWNER_GUARD, NamedJob, named}, + vars::{ + JobOutput, StepOutput, WorkflowInput, WorkflowSecret, one_workflow_per_non_main_branch, + }, +}; + +const BUMPVERSION_CONFIG: &str = indoc! {r#" + [bumpversion] + current_version = "$OLD_VERSION" + + [bumpversion:file:Cargo.toml] + + [bumpversion:file:extension.toml] + "# +}; + +const VERSION_CHECK: &str = r#"sed -n 's/version = \"\(.*\)\"/\1/p' < extension.toml"#; + +// This is used by various extensions repos in the zed-extensions org to bump extension versions. +pub(crate) fn extension_bump() -> Workflow { + let bump_type = WorkflowInput::string("bump-type", Some("patch".to_owned())); + + let app_id = WorkflowSecret::new("app-id", "The app ID used to create the PR"); + let app_secret = + WorkflowSecret::new("app-secret", "The app secret for the corresponding app ID"); + + let test_extension = extension_tests::check_extension(); + let (check_bump_needed, needs_bump) = check_bump_needed(); + let bump_version = bump_extension_version( + &[&test_extension, &check_bump_needed], + &bump_type, + needs_bump.as_job_output(&check_bump_needed), + &app_id, + &app_secret, + ); + + named::workflow() + .add_event( + Event::default().workflow_call( + WorkflowCall::default() + .add_input(bump_type.name, bump_type.call_input()) + .secrets([ + (app_id.name.to_owned(), app_id.secret_configuration()), + ( + app_secret.name.to_owned(), + app_secret.secret_configuration(), + ), + ]), + ), + ) + .concurrency(one_workflow_per_non_main_branch()) + .add_env(("CARGO_TERM_COLOR", "always")) + .add_env(("RUST_BACKTRACE", 1)) + .add_env(("CARGO_INCREMENTAL", 0)) + .add_env(( + "ZED_EXTENSION_CLI_SHA", + extension_tests::ZED_EXTENSION_CLI_SHA, + )) + .add_job(test_extension.name, test_extension.job) + .add_job(check_bump_needed.name, check_bump_needed.job) + .add_job(bump_version.name, bump_version.job) +} + +fn check_bump_needed() -> (NamedJob, StepOutput) { + let (compare_versions, version_changed) = compare_versions(); + + let job = Job::default() + .with_repository_owner_guard() + .outputs([(version_changed.name.to_owned(), version_changed.to_string())]) + .runs_on(runners::LINUX_SMALL) + .timeout_minutes(1u32) + .add_step(steps::checkout_repo().add_with(("fetch-depth", 10))) + .add_step(compare_versions); + + (named::job(job), version_changed) +} + +/// Compares the current and previous commit and checks whether versions changed inbetween. +fn compare_versions() -> (Step, StepOutput) { + let check_needs_bump = named::bash(format!( + indoc! { + r#" + CURRENT_VERSION="$({})" + + git checkout "$(git log -1 --format=%H)"~1 + + PREV_COMMIT_VERSION="$({})" + + [[ "$CURRENT_VERSION" == "$PREV_COMMIT_VERSION" ]] && \ + echo "needs_bump=true" >> "$GITHUB_OUTPUT" || \ + echo "needs_bump=false" >> "$GITHUB_OUTPUT" + + "# + }, + VERSION_CHECK, VERSION_CHECK + )) + .id("compare-versions-check"); + + let needs_bump = StepOutput::new(&check_needs_bump, "needs_bump"); + + (check_needs_bump, needs_bump) +} + +fn bump_extension_version( + dependencies: &[&NamedJob], + bump_type: &WorkflowInput, + needs_bump: JobOutput, + app_id: &WorkflowSecret, + app_secret: &WorkflowSecret, +) -> NamedJob { + let (generate_token, generated_token) = generate_token(app_id, app_secret); + let (bump_version, old_version, new_version) = bump_version(bump_type); + + let job = steps::dependant_job(dependencies) + .cond(Expression::new(format!( + "{DEFAULT_REPOSITORY_OWNER_GUARD} && {} == 'true'", + needs_bump.expr(), + ))) + .runs_on(runners::LINUX_LARGE) + .timeout_minutes(1u32) + .add_step(generate_token) + .add_step(steps::checkout_repo()) + .add_step(install_bump_2_version()) + .add_step(bump_version) + .add_step(create_pull_request( + old_version, + new_version, + generated_token, + )); + + named::job(job) +} + +fn generate_token(app_id: &WorkflowSecret, app_secret: &WorkflowSecret) -> (Step, StepOutput) { + let step = named::uses("actions", "create-github-app-token", "v2") + .id("generate-token") + .add_with( + Input::default() + .add("app-id", app_id.to_string()) + .add("private-key", app_secret.to_string()), + ); + + let generated_token = StepOutput::new(&step, "token"); + + (step, generated_token) +} + +fn install_bump_2_version() -> Step { + named::run(runners::Platform::Linux, "pip install bump2version") +} + +fn bump_version(bump_type: &WorkflowInput) -> (Step, StepOutput, StepOutput) { + let step = named::bash(format!( + indoc! {r#" + OLD_VERSION="$({})" + + cat < .bumpversion.cfg + {} + EOF + + bump2version --verbose {} + NEW_VERSION="$({})" + cargo update --workspace + + rm .bumpversion.cfg + + echo "old_version=${{OLD_VERSION}}" >> "$GITHUB_OUTPUT" + echo "new_version=${{NEW_VERSION}}" >> "$GITHUB_OUTPUT" + "# + }, + VERSION_CHECK, BUMPVERSION_CONFIG, bump_type, VERSION_CHECK + )) + .id("bump-version"); + + let old_version = StepOutput::new(&step, "old_version"); + let new_version = StepOutput::new(&step, "new_version"); + (step, old_version, new_version) +} + +fn create_pull_request( + old_version: StepOutput, + new_version: StepOutput, + generated_token: StepOutput, +) -> Step { + let formatted_version = format!("v{}", new_version); + + named::uses("peter-evans", "create-pull-request", "v7").with( + Input::default() + .add("title", format!("Bump version to {}", new_version)) + .add( + "body", + format!( + "This PR bumps the version of this extension to {}", + formatted_version + ), + ) + .add( + "commit-message", + format!("Bump version to {}", formatted_version), + ) + .add("branch", format!("bump-from-{}", old_version)) + .add( + "committer", + "zed-zippy[bot] <234243425+zed-zippy[bot]@users.noreply.github.com>", + ) + .add("base", "main") + .add("delete-branch", true) + .add("token", generated_token.to_string()) + .add("sign-commits", true), + ) +} diff --git a/tooling/xtask/src/tasks/workflows/extension_tests.rs b/tooling/xtask/src/tasks/workflows/extension_tests.rs index 4ee094fd37608c2427037effac3a6afa182014ba..8ea1435292372e33d5f98d1b3a5d5db0582a6a46 100644 --- a/tooling/xtask/src/tasks/workflows/extension_tests.rs +++ b/tooling/xtask/src/tasks/workflows/extension_tests.rs @@ -9,7 +9,7 @@ use crate::tasks::workflows::{ }; const RUN_TESTS_INPUT: &str = "run_tests"; -const ZED_EXTENSION_CLI_SHA: &str = "7cfce605704d41ca247e3f84804bf323f6c6caaf"; +pub(crate) const ZED_EXTENSION_CLI_SHA: &str = "7cfce605704d41ca247e3f84804bf323f6c6caaf"; // This is used by various extensions repos in the zed-extensions org to run automated tests. pub(crate) fn extension_tests() -> Workflow { @@ -77,7 +77,7 @@ fn check_rust() -> NamedJob { named::job(job) } -fn check_extension() -> NamedJob { +pub(crate) fn check_extension() -> NamedJob { let (cache_download, cache_hit) = cache_zed_extension_cli(); let job = Job::default() .with_repository_owner_guard() diff --git a/tooling/xtask/src/tasks/workflows/run_agent_evals.rs b/tooling/xtask/src/tasks/workflows/run_agent_evals.rs index ec9b70a2db9049b62676b43d614818374e0930a1..220d3872f72326f42845622b5e3c61f4819f4550 100644 --- a/tooling/xtask/src/tasks/workflows/run_agent_evals.rs +++ b/tooling/xtask/src/tasks/workflows/run_agent_evals.rs @@ -3,12 +3,12 @@ use gh_workflow::{Event, Expression, Job, Run, Schedule, Step, Use, Workflow, Wo use crate::tasks::workflows::{ runners::{self, Platform}, steps::{self, FluentBuilder as _, NamedJob, named, setup_cargo_config}, - vars::{self, Input}, + vars::{self, WorkflowInput}, }; pub(crate) fn run_agent_evals() -> Workflow { let agent_evals = agent_evals(); - let model_name = Input::string("model_name", None); + let model_name = WorkflowInput::string("model_name", None); named::workflow() .on(Event::default().workflow_dispatch( @@ -29,8 +29,8 @@ pub(crate) fn run_agent_evals() -> Workflow { } pub(crate) fn run_unit_evals() -> Workflow { - let model_name = Input::string("model_name", None); - let commit_sha = Input::string("commit_sha", None); + let model_name = WorkflowInput::string("model_name", None); + let commit_sha = WorkflowInput::string("commit_sha", None); let unit_evals = named::job(unit_evals(Some(&commit_sha))); @@ -117,7 +117,7 @@ fn cron_unit_evals() -> NamedJob { named::job(unit_evals(None).add_step(send_failure_to_slack())) } -fn unit_evals(commit: Option<&Input>) -> Job { +fn unit_evals(commit: Option<&WorkflowInput>) -> Job { let script_step = add_api_keys(steps::script("./script/run-unit-evals")); Job::default() diff --git a/tooling/xtask/src/tasks/workflows/steps.rs b/tooling/xtask/src/tasks/workflows/steps.rs index 910b344cb7319e4f58911b3025632e560553716a..e20dafe18a660a0067708cc1e9d15d59572e5f53 100644 --- a/tooling/xtask/src/tasks/workflows/steps.rs +++ b/tooling/xtask/src/tasks/workflows/steps.rs @@ -142,9 +142,13 @@ pub struct NamedJob { // } // } +pub(crate) const DEFAULT_REPOSITORY_OWNER_GUARD: &str = + "(github.repository_owner == 'zed-industries' || github.repository_owner == 'zed-extensions')"; + pub fn repository_owner_guard_expression(trigger_always: bool) -> Expression { Expression::new(format!( - "(github.repository_owner == 'zed-industries' || github.repository_owner == 'zed-extensions'){}", + "{}{}", + DEFAULT_REPOSITORY_OWNER_GUARD, trigger_always.then_some(" && always()").unwrap_or_default() )) } @@ -248,8 +252,10 @@ pub mod named { /// Returns a bash-script step with the same name as the enclosing function. /// (You shouldn't inline this function into the workflow definition, you must /// wrap it in a new function.) - pub fn bash(script: &str) -> Step { - Step::new(function_name(1)).run(script).shell(BASH_SHELL) + pub fn bash(script: impl AsRef) -> Step { + Step::new(function_name(1)) + .run(script.as_ref()) + .shell(BASH_SHELL) } /// Returns a pwsh-script step with the same name as the enclosing function. diff --git a/tooling/xtask/src/tasks/workflows/vars.rs b/tooling/xtask/src/tasks/workflows/vars.rs index bbb98af757dd9b794ae7c57d6ddb4f1d3d10019d..8dae64a1ea10ca891d23cabb989f5073ddd1755d 100644 --- a/tooling/xtask/src/tasks/workflows/vars.rs +++ b/tooling/xtask/src/tasks/workflows/vars.rs @@ -1,6 +1,9 @@ use std::cell::RefCell; -use gh_workflow::{Concurrency, Env, Expression, Step, WorkflowDispatchInput}; +use gh_workflow::{ + Concurrency, Env, Expression, Step, WorkflowCallInput, WorkflowCallSecret, + WorkflowDispatchInput, +}; use crate::tasks::workflows::{runners::Platform, steps::NamedJob}; @@ -132,7 +135,7 @@ impl PathCondition { } pub(crate) struct StepOutput { - name: &'static str, + pub name: &'static str, step_id: String, } @@ -151,6 +154,13 @@ impl StepOutput { pub fn expr(&self) -> String { format!("steps.{}.outputs.{}", self.step_id, self.name) } + + pub fn as_job_output(self, job: &NamedJob) -> JobOutput { + JobOutput { + job_name: job.name.clone(), + name: self.name, + } + } } impl serde::Serialize for StepOutput { @@ -164,17 +174,43 @@ impl serde::Serialize for StepOutput { impl std::fmt::Display for StepOutput { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "${{{{ steps.{}.outputs.{} }}}}", self.step_id, self.name) + write!(f, "${{{{ {} }}}}", self.expr()) + } +} + +pub(crate) struct JobOutput { + job_name: String, + name: &'static str, +} + +impl JobOutput { + pub fn expr(&self) -> String { + format!("needs.{}.outputs.{}", self.job_name, self.name) + } +} + +impl serde::Serialize for JobOutput { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + serializer.serialize_str(&self.to_string()) + } +} + +impl std::fmt::Display for JobOutput { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "${{{{ {} }}}}", self.expr()) } } -pub struct Input { +pub struct WorkflowInput { pub input_type: &'static str, pub name: &'static str, pub default: Option, } -impl Input { +impl WorkflowInput { pub fn string(name: &'static str, default: Option) -> Self { Self { input_type: "string", @@ -191,15 +227,62 @@ impl Input { default: self.default.clone(), } } + + pub fn call_input(&self) -> WorkflowCallInput { + WorkflowCallInput { + description: self.name.to_owned(), + required: self.default.is_none(), + input_type: self.input_type.to_owned(), + default: self.default.clone(), + } + } } -impl std::fmt::Display for Input { +impl std::fmt::Display for WorkflowInput { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "${{{{ inputs.{} }}}}", self.name) } } -impl serde::Serialize for Input { +impl serde::Serialize for WorkflowInput { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + serializer.serialize_str(&self.to_string()) + } +} + +pub(crate) struct WorkflowSecret { + pub name: &'static str, + description: String, + required: bool, +} + +impl WorkflowSecret { + pub fn new(name: &'static str, description: impl ToString) -> Self { + Self { + name, + description: description.to_string(), + required: true, + } + } + + pub fn secret_configuration(&self) -> WorkflowCallSecret { + WorkflowCallSecret { + description: self.description.clone(), + required: self.required, + } + } +} + +impl std::fmt::Display for WorkflowSecret { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "${{{{ secrets.{} }}}}", self.name) + } +} + +impl serde::Serialize for WorkflowSecret { fn serialize(&self, serializer: S) -> Result where S: serde::Serializer,