From d2cb9a13b86e9c6e2e4de077383eaaa4c6d0ebea Mon Sep 17 00:00:00 2001 From: "zed-zippy[bot]" <234243425+zed-zippy[bot]@users.noreply.github.com> Date: Thu, 6 Nov 2025 06:47:58 +0000 Subject: [PATCH] Refresh zed.dev releases page after releases (#42060) (cherry-pick to preview) (#42063) Cherry-pick of #42060 to preview ---- Release Notes: - N/A Co-authored-by: Conrad Irwin --- .github/workflows/after_release.yml | 69 ++++++++++ .../workflows/community_release_actions.yml | 93 ------------- Cargo.lock | 1 + tooling/xtask/Cargo.toml | 1 + tooling/xtask/src/tasks/workflows.rs | 2 + .../src/tasks/workflows/after_release.rs | 123 ++++++++++++++++++ tooling/xtask/src/tasks/workflows/vars.rs | 21 +++ 7 files changed, 217 insertions(+), 93 deletions(-) create mode 100644 .github/workflows/after_release.yml delete mode 100644 .github/workflows/community_release_actions.yml create mode 100644 tooling/xtask/src/tasks/workflows/after_release.rs diff --git a/.github/workflows/after_release.yml b/.github/workflows/after_release.yml new file mode 100644 index 0000000000000000000000000000000000000000..6732a0921444fe3b2eeb0541e89eac0009956a7f --- /dev/null +++ b/.github/workflows/after_release.yml @@ -0,0 +1,69 @@ +# Generated from xtask::workflows::after_release +# Rebuild with `cargo xtask workflows`. +name: after_release +on: + release: + types: + - published +jobs: + rebuild_releases_page: + if: github.repository_owner == 'zed-industries' + runs-on: namespace-profile-2x4-ubuntu-2404 + steps: + - name: after_release::rebuild_releases_page + run: 'curl https://zed.dev/api/revalidate-releases -H "Authorization: Bearer ${RELEASE_NOTES_API_TOKEN}"' + shell: bash -euxo pipefail {0} + env: + RELEASE_NOTES_API_TOKEN: ${{ secrets.RELEASE_NOTES_API_TOKEN }} + post_to_discord: + needs: + - rebuild_releases_page + if: github.repository_owner == 'zed-industries' + runs-on: namespace-profile-2x4-ubuntu-2404 + steps: + - id: get-release-url + name: after_release::post_to_discord::get_release_url + run: | + if [ "${{ github.event.release.prerelease }}" == "true" ]; then + URL="https://zed.dev/releases/preview" + else + URL="https://zed.dev/releases/stable" + fi + + echo "URL=$URL" >> "$GITHUB_OUTPUT" + shell: bash -euxo pipefail {0} + - id: get-content + name: after_release::post_to_discord::get_content + uses: 2428392/gh-truncate-string-action@b3ff790d21cf42af3ca7579146eedb93c8fb0757 + with: + stringToTruncate: | + 📣 Zed [${{ github.event.release.tag_name }}](<${{ steps.get-release-url.outputs.URL }}>) was just released! + + ${{ github.event.release.body }} + maxLength: 2000 + truncationSymbol: '...' + - name: after_release::post_to_discord::discord_webhook_action + uses: tsickert/discord-webhook@c840d45a03a323fbc3f7507ac7769dbd91bfb164 + with: + webhook-url: ${{ secrets.DISCORD_WEBHOOK_RELEASE_NOTES }} + content: ${{ steps.get-content.outputs.string }} + publish_winget: + runs-on: namespace-profile-2x4-ubuntu-2404 + steps: + - id: set-package-name + name: after_release::publish_winget::set_package_name + run: | + if [ "${{ github.event.release.prerelease }}" == "true" ]; then + PACKAGE_NAME=ZedIndustries.Zed.Preview + else + PACKAGE_NAME=ZedIndustries.Zed + fi + + echo "PACKAGE_NAME=$PACKAGE_NAME" >> "$GITHUB_OUTPUT" + shell: bash -euxo pipefail {0} + - name: after_release::publish_winget::winget_releaser + uses: vedantmgoyal9/winget-releaser@19e706d4c9121098010096f9c495a70a7518b30f + with: + identifier: ${{ steps.set-package-name.outputs.PACKAGE_NAME }} + max-versions-to-keep: 5 + token: ${{ secrets.WINGET_TOKEN }} diff --git a/.github/workflows/community_release_actions.yml b/.github/workflows/community_release_actions.yml deleted file mode 100644 index 7724aa2096cfa31c0586c9a43678a805443b259a..0000000000000000000000000000000000000000 --- a/.github/workflows/community_release_actions.yml +++ /dev/null @@ -1,93 +0,0 @@ -# IF YOU UPDATE THE NAME OF ANY GITHUB SECRET, YOU MUST CHERRY PICK THE COMMIT -# TO BOTH STABLE AND PREVIEW CHANNELS - -name: Release Actions - -on: - release: - types: [published] - -jobs: - discord_release: - if: github.repository_owner == 'zed-industries' - runs-on: ubuntu-latest - steps: - - name: Get release URL - id: get-release-url - run: | - if [ "${{ github.event.release.prerelease }}" == "true" ]; then - URL="https://zed.dev/releases/preview" - else - URL="https://zed.dev/releases/stable" - fi - - echo "URL=$URL" >> "$GITHUB_OUTPUT" - - name: Get content - uses: 2428392/gh-truncate-string-action@b3ff790d21cf42af3ca7579146eedb93c8fb0757 # v1.4.1 - id: get-content - with: - stringToTruncate: | - 📣 Zed [${{ github.event.release.tag_name }}](<${{ steps.get-release-url.outputs.URL }}>) was just released! - - ${{ github.event.release.body }} - maxLength: 2000 - truncationSymbol: "..." - - name: Discord Webhook Action - uses: tsickert/discord-webhook@c840d45a03a323fbc3f7507ac7769dbd91bfb164 # v5.3.0 - with: - webhook-url: ${{ secrets.DISCORD_WEBHOOK_RELEASE_NOTES }} - content: ${{ steps.get-content.outputs.string }} - - publish-winget: - runs-on: - - ubuntu-latest - steps: - - name: Set Package Name - id: set-package-name - run: | - if [ "${{ github.event.release.prerelease }}" == "true" ]; then - PACKAGE_NAME=ZedIndustries.Zed.Preview - else - PACKAGE_NAME=ZedIndustries.Zed - fi - - echo "PACKAGE_NAME=$PACKAGE_NAME" >> "$GITHUB_OUTPUT" - - uses: vedantmgoyal9/winget-releaser@19e706d4c9121098010096f9c495a70a7518b30f # v2 - with: - identifier: ${{ steps.set-package-name.outputs.PACKAGE_NAME }} - max-versions-to-keep: 5 - token: ${{ secrets.WINGET_TOKEN }} - - send_release_notes_email: - if: false && github.repository_owner == 'zed-industries' && !github.event.release.prerelease - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 - with: - fetch-depth: 0 - - - name: Check if release was promoted from preview - id: check-promotion-from-preview - run: | - VERSION="${{ github.event.release.tag_name }}" - PREVIEW_TAG="${VERSION}-pre" - - if git rev-parse "$PREVIEW_TAG" > /dev/null 2>&1; then - echo "was_promoted_from_preview=true" >> "$GITHUB_OUTPUT" - else - echo "was_promoted_from_preview=false" >> "$GITHUB_OUTPUT" - fi - - - name: Send release notes email - if: steps.check-promotion-from-preview.outputs.was_promoted_from_preview == 'true' - run: | - TAG="${{ github.event.release.tag_name }}" - cat << 'EOF' > release_body.txt - ${{ github.event.release.body }} - EOF - jq -n --arg tag "$TAG" --rawfile body release_body.txt '{version: $tag, markdown_body: $body}' \ - > release_data.json - curl -X POST "https://zed.dev/api/send_release_notes_email" \ - -H "Authorization: Bearer ${{ secrets.RELEASE_NOTES_API_TOKEN }}" \ - -H "Content-Type: application/json" \ - -d @release_data.json diff --git a/Cargo.lock b/Cargo.lock index b050f0ed51453954c7b2a2047f4075b8d98bab8c..e483d2cc846b1a340ee125b3b167958745572a1f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -20951,6 +20951,7 @@ dependencies = [ "gh-workflow", "indexmap 2.11.4", "indoc", + "serde", "toml 0.8.23", "toml_edit 0.22.27", ] diff --git a/tooling/xtask/Cargo.toml b/tooling/xtask/Cargo.toml index 7fc03a563e0a0375b0d3003349530f9b738964d9..38c491dac668cf008aaee38acb30f870bcf09852 100644 --- a/tooling/xtask/Cargo.toml +++ b/tooling/xtask/Cargo.toml @@ -17,5 +17,6 @@ clap = { workspace = true, features = ["derive"] } toml.workspace = true indoc.workspace = true indexmap.workspace = true +serde.workspace = true toml_edit.workspace = true gh-workflow.workspace = true diff --git a/tooling/xtask/src/tasks/workflows.rs b/tooling/xtask/src/tasks/workflows.rs index 4cc1f5174a754df800fcae768bfe7ff1645032d3..bf6a332075c52cd08dcc44d73fc37239bd60a740 100644 --- a/tooling/xtask/src/tasks/workflows.rs +++ b/tooling/xtask/src/tasks/workflows.rs @@ -3,6 +3,7 @@ use clap::Parser; use std::fs; use std::path::Path; +mod after_release; mod cherry_pick; mod compare_perf; mod danger; @@ -33,6 +34,7 @@ pub fn run_workflows(_: GenerateWorkflowArgs) -> Result<()> { ("compare_perf.yml", compare_perf::compare_perf()), ("run_unit_evals.yml", run_agent_evals::run_unit_evals()), ("run_agent_evals.yml", run_agent_evals::run_agent_evals()), + ("after_release.yml", after_release::after_release()), ]; fs::create_dir_all(dir) .with_context(|| format!("Failed to create directory: {}", dir.display()))?; diff --git a/tooling/xtask/src/tasks/workflows/after_release.rs b/tooling/xtask/src/tasks/workflows/after_release.rs new file mode 100644 index 0000000000000000000000000000000000000000..7d82b53eee480a1e381cceaadc649a57fa8e7e39 --- /dev/null +++ b/tooling/xtask/src/tasks/workflows/after_release.rs @@ -0,0 +1,123 @@ +use gh_workflow::*; + +use crate::tasks::workflows::{ + runners, + steps::{NamedJob, dependant_job, named}, + vars::{self, StepOutput}, +}; + +pub fn after_release() -> Workflow { + let refresh_zed_dev = rebuild_releases_page(); + let post_to_discord = post_to_discord(&[&refresh_zed_dev]); + let publish_winget = publish_winget(); + + named::workflow() + .on(Event::default().release(Release::default().types(vec![ReleaseType::Published]))) + .add_job(refresh_zed_dev.name, refresh_zed_dev.job) + .add_job(post_to_discord.name, post_to_discord.job) + .add_job(publish_winget.name, publish_winget.job) +} + +fn rebuild_releases_page() -> NamedJob { + named::job( + Job::default() + .runs_on(runners::LINUX_SMALL) + .cond(Expression::new( + "github.repository_owner == 'zed-industries'", + )) + .add_step(named::bash( + "curl https://zed.dev/api/revalidate-releases -H \"Authorization: Bearer ${RELEASE_NOTES_API_TOKEN}\"", + ).add_env(("RELEASE_NOTES_API_TOKEN", vars::RELEASE_NOTES_API_TOKEN))), + ) +} + +fn post_to_discord(deps: &[&NamedJob]) -> NamedJob { + fn get_release_url() -> Step { + named::bash(indoc::indoc! {r#" + if [ "${{ github.event.release.prerelease }}" == "true" ]; then + URL="https://zed.dev/releases/preview" + else + URL="https://zed.dev/releases/stable" + fi + + echo "URL=$URL" >> "$GITHUB_OUTPUT" + "#}) + .id("get-release-url") + } + + fn get_content() -> Step { + named::uses( + "2428392", + "gh-truncate-string-action", + "b3ff790d21cf42af3ca7579146eedb93c8fb0757", // v1.4.1 + ) + .id("get-content") + .add_with(( + "stringToTruncate", + indoc::indoc! {r#" + 📣 Zed [${{ github.event.release.tag_name }}](<${{ steps.get-release-url.outputs.URL }}>) was just released! + + ${{ github.event.release.body }} + "#}, + )) + .add_with(("maxLength", 2000)) + .add_with(("truncationSymbol", "...")) + } + + fn discord_webhook_action() -> Step { + named::uses( + "tsickert", + "discord-webhook", + "c840d45a03a323fbc3f7507ac7769dbd91bfb164", // v5.3.0 + ) + .add_with(("webhook-url", vars::DISCORD_WEBHOOK_RELEASE_NOTES)) + .add_with(("content", "${{ steps.get-content.outputs.string }}")) + } + let job = dependant_job(deps) + .runs_on(runners::LINUX_SMALL) + .cond(Expression::new( + "github.repository_owner == 'zed-industries'", + )) + .add_step(get_release_url()) + .add_step(get_content()) + .add_step(discord_webhook_action()); + named::job(job) +} + +fn publish_winget() -> NamedJob { + fn set_package_name() -> (Step, StepOutput) { + let step = named::bash(indoc::indoc! {r#" + if [ "${{ github.event.release.prerelease }}" == "true" ]; then + PACKAGE_NAME=ZedIndustries.Zed.Preview + else + PACKAGE_NAME=ZedIndustries.Zed + fi + + echo "PACKAGE_NAME=$PACKAGE_NAME" >> "$GITHUB_OUTPUT" + "#}) + .id("set-package-name"); + + let output = StepOutput::new(&step, "PACKAGE_NAME"); + (step, output) + } + + fn winget_releaser(package_name: &StepOutput) -> Step { + named::uses( + "vedantmgoyal9", + "winget-releaser", + "19e706d4c9121098010096f9c495a70a7518b30f", // v2 + ) + .add_with(("identifier", package_name.to_string())) + .add_with(("max-versions-to-keep", 5)) + .add_with(("token", vars::WINGET_TOKEN)) + } + + let (set_package_name, package_name) = set_package_name(); + + named::job( + Job::default() + .runs_on(runners::LINUX_SMALL) + .add_step(set_package_name) + .add_step(winget_releaser(&package_name)), + ) +} diff --git a/tooling/xtask/src/tasks/workflows/vars.rs b/tooling/xtask/src/tasks/workflows/vars.rs index 640efdf6c12553fe5494b88ec5816b498016f6ec..b3eb86554a21e6d921d9d2eb23a5467a7506906c 100644 --- a/tooling/xtask/src/tasks/workflows/vars.rs +++ b/tooling/xtask/src/tasks/workflows/vars.rs @@ -36,6 +36,9 @@ secret!(ZED_SENTRY_MINIDUMP_ENDPOINT); secret!(SLACK_APP_ZED_UNIT_EVALS_BOT_TOKEN); secret!(ZED_ZIPPY_APP_ID); secret!(ZED_ZIPPY_APP_PRIVATE_KEY); +secret!(DISCORD_WEBHOOK_RELEASE_NOTES); +secret!(WINGET_TOKEN); +secret!(RELEASE_NOTES_API_TOKEN); // todo(ci) make these secrets too... var!(AZURE_SIGNING_ACCOUNT_NAME); @@ -136,6 +139,15 @@ impl StepOutput { } } +impl serde::Serialize for StepOutput { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + serializer.serialize_str(&self.to_string()) + } +} + 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) @@ -173,6 +185,15 @@ impl std::fmt::Display for Input { } } +impl serde::Serialize for Input { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + serializer.serialize_str(&self.to_string()) + } +} + pub mod assets { // NOTE: these asset names also exist in the zed.dev codebase. pub const MAC_AARCH64: &str = "Zed-aarch64.dmg";