# A reusable workflow that encapsulates all of the logic surrounding building # all bundles for a single release channel, either new releases (when run on the # `master` branch) or updated release candidates (when run within a `*_release` # branch). # # This can either be triggered: # * Indirectly, using workflow_call, for creating real releases. # * Directly, using workflow_dispatch, for testing. By default, this will not create GitHub or Sentry releases, or upload to GCS. name: Create Release on: workflow_call: inputs: channel: description: The channel to create a release or release candidate for. required: true type: string build_linux: description: Build Linux artifacts. type: boolean default: true build_windows: description: Build Windows artifacts. type: boolean default: true build_macos: description: Build macOS artifacts. type: boolean default: true build_web: description: Build web artifacts. type: boolean default: true should_publish: description: Publish this release type: boolean default: true workflow_dispatch: inputs: channel: type: string required: true build_linux: description: Build Linux artifacts. type: boolean default: true build_windows: description: Build Windows artifacts. type: boolean default: true build_macos: description: Build macOS artifacts. type: boolean default: true build_web: description: Build web artifacts. type: boolean default: true env: CARGO_TERM_COLOR: always CONFIG_FILE: ".github/workflows/release_configurations.json" jobs: # Perform all once-per-release steps. Dependent jobs will build release # assets and upload them as appropriate. prepare_release: name: Prepare release runs-on: ubuntu-latest outputs: release_branch: ${{ steps.create_branch_and_tag.outputs.branch }} release_tag: ${{ steps.create_branch_and_tag.outputs.tag }} should_publish: ${{ steps.set_publish.outputs.should_publish }} steps: - name: Checkout sources uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Get channel configuration id: get-config uses: ./.github/actions/get_channel_config/ with: config_file: ${{ env.CONFIG_FILE }} channel: ${{ inputs.channel }} - name: Set publish flag based on the workflow trigger id: set_publish run: | # The should_publish input is only set on the workflow_call trigger. # This means it's not possible to trigger publishing via workflow_dispatch, and # publishing is enabled by default when called for a new release or RC. if [[ "${{ inputs.should_publish }}" == "true" ]]; then echo "should_publish=true" >> $GITHUB_OUTPUT else echo "should_publish=false" >> $GITHUB_OUTPUT fi shell: bash - name: Update branch and tag id: create_branch_and_tag run: | if [[ "${{ steps.set_publish.outputs.should_publish }}" == "true" ]]; then GIT_BRANCH_NAME="${GITHUB_REF#refs/heads/}" source ./script/create_release_tag_and_branch --channel $CHANNEL --branch-name "$GIT_BRANCH_NAME" echo "branch=$(git branch --show-current)" >> $GITHUB_OUTPUT echo "tag=$tag" >> $GITHUB_OUTPUT else echo "branch=$(git branch --show-current)" >> $GITHUB_OUTPUT # Use a fake version tag that will pass package validation. echo "tag=v0.$GITHUB_SHA" >> $GITHUB_OUTPUT fi shell: bash env: CHANNEL: ${{ steps.get-config.outputs.channel }} - name: Create GitHub release if: ${{ steps.set_publish.outputs.should_publish == 'true' }} id: create_release uses: softprops/action-gh-release@da05d552573ad5aba039eaac05058a918a7bf631 # v2.2.2 with: name: ${{ steps.get-config.outputs.release_base_name }} ${{ steps.create_branch_and_tag.outputs.tag }} tag_name: ${{ steps.create_branch_and_tag.outputs.tag }} body: ${{ steps.get-config.outputs.release_body_text }} draft: false prerelease: ${{ steps.get-config.outputs.is_prerelease }} token: ${{ secrets.GITHUB_TOKEN }} - name: Create Sentry release if: ${{ steps.set_publish.outputs.should_publish == 'true' }} uses: getsentry/action-release@5657c9e888b4e2cc85f4d29143ea4131fde4a73a # v3.6.0 env: SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_RELEASE_TOKEN }} SENTRY_ORG: warpdotdev SENTRY_PROJECT: ${{ steps.get-config.outputs.sentry_project }} with: environment: ${{ steps.get-config.outputs.sentry_environment }} version: ${{ steps.create_branch_and_tag.outputs.tag }} release_macos_single_arch: name: Build Release (macOS ${{ matrix.dmg_name_suffix }}) runs-on: macos-26-xlarge needs: prepare_release if: ${{ inputs.build_macos != false }} timeout-minutes: 60 strategy: fail-fast: false matrix: include: - arch: aarch64 dmg_name_suffix: arm64 - arch: x86_64 dmg_name_suffix: x86_64 steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - uses: ./.github/actions/prepare_environment with: target_os: macos is_self_hosted: false ref: ${{ needs.prepare_release.outputs.release_branch }} install_release_deps: true - name: Ensure rust target is installed run: rustup target add ${{ matrix.arch }}-apple-darwin shell: bash # Install go toolchain, as it may be needed for some build steps. - name: Setup Go uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6 with: go-version: stable - name: Get channel configuration id: get-config uses: ./.github/actions/get_channel_config/ with: config_file: ${{ env.CONFIG_FILE }} channel: ${{ inputs.channel }} - name: Install cargo-bundle run: script/install_cargo_bundle - name: Install create-dmg run: brew install create-dmg - name: Build ${{ matrix.arch }} bundle id: bundle_app run: | script/bundle --read-passwords-from-env --channel $CHANNEL --arch ${{ matrix.arch }} --dmg-name-suffix "${{ matrix.dmg_name_suffix }}" shell: bash env: CHANNEL: ${{ steps.get-config.outputs.channel }} GIT_RELEASE_TAG: ${{ needs.prepare_release.outputs.release_tag }} WARP_DEVELOPER_ID_CERT: ${{ secrets.WARP_DEVELOPER_ID_CERT }} WARP_DEVELOPER_ID_CERT_PASSWORD: ${{ secrets.WARP_DEVELOPER_ID_CERT_PASSWORD }} WARP_CODESIGN_KEYCHAIN_PASSWORD: ${{ secrets.WARP_CODESIGN_KEYCHAIN_PASSWORD }} WARP_NOTARIZATION_APPLE_ID: ${{ secrets.WARP_NOTARIZATION_APPLE_ID }} WARP_NOTARIZATION_PASSWORD: ${{ secrets.WARP_NOTARIZATION_PASSWORD }} - name: Add DMG to GitHub release assets if: ${{ needs.prepare_release.outputs.should_publish == 'true' }} uses: softprops/action-gh-release@da05d552573ad5aba039eaac05058a918a7bf631 # v2.2.2 with: tag_name: ${{ needs.prepare_release.outputs.release_tag }} files: ${{ steps.bundle_app.outputs.dmg_path }} token: ${{ secrets.GITHUB_TOKEN }} - uses: google-github-actions/auth@7c6bc770dae815cd3e89ee6cdf493a5fab2cc093 # v3.0.0 if: ${{ needs.prepare_release.outputs.should_publish == 'true' }} with: credentials_json: ${{ secrets.GOOGLE_APPLICATION_CREDENTIALS }} - name: Upload DMG to Google Cloud Storage if: ${{ needs.prepare_release.outputs.should_publish == 'true' }} uses: google-github-actions/upload-cloud-storage@e95a15f226403ed658d3e65f40205649f342ba2c # v1 with: path: ${{ steps.bundle_app.outputs.dmg_path }} destination: warp-releases/${{ steps.get-config.outputs.channel }}/${{ needs.prepare_release.outputs.release_tag }} headers: |- cache-control: ${{ steps.get-config.outputs.gcs_cache_control_value }} - name: Upload DMG as workflow artifact if: ${{ needs.prepare_release.outputs.should_publish != 'true' }} uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6 with: name: release-macos-${{ matrix.arch }}-${{ steps.get-config.outputs.channel }} path: ${{ steps.bundle_app.outputs.dmg_path }} - name: Set up Sentry CLI if: ${{ needs.prepare_release.outputs.should_publish == 'true' }} uses: matbour/setup-sentry-cli@3e938c54b3018bdd019973689ef984e033b0454b # v2.0.0 with: token: ${{ secrets.SENTRY_RELEASE_TOKEN }} organization: warpdotdev project: ${{ steps.get-config.outputs.sentry_project }} - name: Upload debug symbols from frameworks to Sentry if: ${{ needs.prepare_release.outputs.should_publish == 'true' }} run: | script/sentry_upload_dif.sh shell: bash env: DEBUG_FILE_OR_FOLDER_PATH: ${{ steps.bundle_app.outputs.frameworks_dir }} # Create a tar archive instead of simply using upload-artifact so we can # preserve permissions and symlinks. If we don't preserve symlinks, we'll # break the macOS framework directory structure. - name: Create tar archive with build artifacts run: | tar czfv build-artifacts.tar.gz \ ${{ steps.bundle_app.outputs.binary_path }} \ ${{ steps.bundle_app.outputs.dock_tile_plugin_dir }} \ ${{ steps.bundle_app.outputs.dsym_path }} \ ${{ steps.bundle_app.outputs.dsym_realpath }} \ ${{ steps.bundle_app.outputs.frameworks_dir }} - name: Archive ${{ matrix.arch }} build artifacts for universal build uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6 with: name: macos-${{ matrix.arch }}-${{ steps.get-config.outputs.channel }} retention-days: 1 # We compress the tar archive, so no need to re-compress. compression-level: 0 path: build-artifacts.tar.gz release_macos_universal: name: Build Release (macOS Universal) runs-on: macos-26-xlarge needs: [prepare_release, release_macos_single_arch] if: ${{ inputs.build_macos != false }} timeout-minutes: 60 steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - uses: ./.github/actions/prepare_environment with: target_os: macos is_self_hosted: false ref: ${{ needs.prepare_release.outputs.release_branch }} install_release_deps: true # Install go toolchain, as it may be needed for some build steps. - name: Setup Go uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6 with: go-version: stable - name: Get channel configuration id: get-config uses: ./.github/actions/get_channel_config/ with: config_file: ${{ env.CONFIG_FILE }} channel: ${{ inputs.channel }} - name: Download aarch64 build artifacts uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: macos-aarch64-${{ steps.get-config.outputs.channel }} - name: Extract aarch64 build artifacts run: tar xvfz build-artifacts.tar.gz shell: bash - name: Download x86_64 build artifacts uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: macos-x86_64-${{ steps.get-config.outputs.channel }} - name: Extract x86_64 build artifacts run: tar xvfz build-artifacts.tar.gz shell: bash - name: Install cargo-bundle run: script/install_cargo_bundle - name: Install create-dmg run: brew install create-dmg - name: Bundle universal app id: bundle_app run: | script/bundle --skip-build --read-passwords-from-env --channel $CHANNEL shell: bash env: CHANNEL: ${{ steps.get-config.outputs.channel }} GIT_RELEASE_TAG: ${{ needs.prepare_release.outputs.release_tag }} WARP_DEVELOPER_ID_CERT: ${{ secrets.WARP_DEVELOPER_ID_CERT }} WARP_DEVELOPER_ID_CERT_PASSWORD: ${{ secrets.WARP_DEVELOPER_ID_CERT_PASSWORD }} WARP_CODESIGN_KEYCHAIN_PASSWORD: ${{ secrets.WARP_CODESIGN_KEYCHAIN_PASSWORD }} WARP_NOTARIZATION_APPLE_ID: ${{ secrets.WARP_NOTARIZATION_APPLE_ID }} WARP_NOTARIZATION_PASSWORD: ${{ secrets.WARP_NOTARIZATION_PASSWORD }} - name: Set up Sentry CLI if: ${{ needs.prepare_release.outputs.should_publish == 'true' }} uses: matbour/setup-sentry-cli@3e938c54b3018bdd019973689ef984e033b0454b # v2.0.0 with: token: ${{ secrets.SENTRY_RELEASE_TOKEN }} organization: warpdotdev project: ${{ steps.get-config.outputs.sentry_project }} - name: Upload debugging symbols to Sentry if: ${{ needs.prepare_release.outputs.should_publish == 'true' }} run: | script/sentry_upload_dif.sh shell: bash env: DEBUG_FILE_OR_FOLDER_PATH: ${{ steps.bundle_app.outputs.dsym_folder_path }} - name: Add DMG to GitHub release assets if: ${{ needs.prepare_release.outputs.should_publish == 'true' }} id: upload_github_assets uses: softprops/action-gh-release@da05d552573ad5aba039eaac05058a918a7bf631 # v2.2.2 # Continue if the upload fails; the next step will retry it. continue-on-error: true with: tag_name: ${{ needs.prepare_release.outputs.release_tag }} files: ${{ steps.bundle_app.outputs.dmg_path }} token: ${{ secrets.GITHUB_TOKEN }} - name: Add DMG to GitHub release assets (retry 1/2) id: upload_github_assets_retry # If the first attempt failed, try again. if: ${{ needs.prepare_release.outputs.should_publish == 'true' && steps.upload_github_assets.outcome == 'failure' }} # Continue if the upload fails; the next step will retry it. continue-on-error: true uses: softprops/action-gh-release@da05d552573ad5aba039eaac05058a918a7bf631 # v2.2.2 with: tag_name: ${{ needs.prepare_release.outputs.release_tag }} files: ${{ steps.bundle_app.outputs.dmg_path }} token: ${{ secrets.GITHUB_TOKEN }} - name: Add DMG to GitHub release assets (retry 2/2) id: upload_github_assets_retry2 # If the first attempt failed, try again. if: ${{ needs.prepare_release.outputs.should_publish == 'true' && steps.upload_github_assets_retry.outcome == 'failure' }} uses: softprops/action-gh-release@da05d552573ad5aba039eaac05058a918a7bf631 # v2.2.2 with: tag_name: ${{ needs.prepare_release.outputs.release_tag }} files: ${{ steps.bundle_app.outputs.dmg_path }} token: ${{ secrets.GITHUB_TOKEN }} - uses: google-github-actions/auth@7c6bc770dae815cd3e89ee6cdf493a5fab2cc093 # v3.0.0 if: ${{ needs.prepare_release.outputs.should_publish == 'true' }} with: credentials_json: ${{ secrets.GOOGLE_APPLICATION_CREDENTIALS }} - name: Upload DMG to Google Cloud Storage if: ${{ needs.prepare_release.outputs.should_publish == 'true' }} uses: google-github-actions/upload-cloud-storage@e95a15f226403ed658d3e65f40205649f342ba2c # v1 with: path: ${{ steps.bundle_app.outputs.dmg_path }} destination: warp-releases/${{ steps.get-config.outputs.channel }}/${{ needs.prepare_release.outputs.release_tag }} # IMPORTANT: both the key and value of the cache-control header must be all lowercase; # uppercase values will not be respected by Cloud CDN and using "Cache-Control" will be # ignored by the upload-cloud-storage action. headers: |- cache-control: ${{ steps.get-config.outputs.gcs_cache_control_value }} - name: Upload DMG as workflow artifact if: ${{ needs.prepare_release.outputs.should_publish != 'true' }} uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6 with: name: release-macos-universal-${{ steps.get-config.outputs.channel }} path: ${{ steps.bundle_app.outputs.dmg_path }} release_macos_cli: name: Build Release (macOS CLI ${{ matrix.arch }}) runs-on: macos-26-xlarge needs: prepare_release if: ${{ inputs.build_macos != false }} timeout-minutes: 60 strategy: fail-fast: false matrix: include: - arch: aarch64 - arch: x86_64 steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - uses: ./.github/actions/prepare_environment with: target_os: macos is_self_hosted: false ref: ${{ needs.prepare_release.outputs.release_branch }} install_release_deps: true - name: Ensure rust target is installed run: rustup target add ${{ matrix.arch }}-apple-darwin shell: bash # Install go toolchain, as it may be needed for some build steps. - name: Setup Go uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6 with: go-version: stable - name: Get channel configuration id: get-config uses: ./.github/actions/get_channel_config/ with: config_file: ${{ env.CONFIG_FILE }} channel: ${{ inputs.channel }} - name: Build ${{ matrix.arch }} binary id: bundle_cli run: | script/bundle --read-passwords-from-env --channel $CHANNEL --arch ${{ matrix.arch }} --artifact cli shell: bash env: CHANNEL: ${{ steps.get-config.outputs.channel }} GIT_RELEASE_TAG: ${{ needs.prepare_release.outputs.release_tag }} WARP_DEVELOPER_ID_CERT: ${{ secrets.WARP_DEVELOPER_ID_CERT }} WARP_DEVELOPER_ID_CERT_PASSWORD: ${{ secrets.WARP_DEVELOPER_ID_CERT_PASSWORD }} WARP_CODESIGN_KEYCHAIN_PASSWORD: ${{ secrets.WARP_CODESIGN_KEYCHAIN_PASSWORD }} WARP_NOTARIZATION_APPLE_ID: ${{ secrets.WARP_NOTARIZATION_APPLE_ID }} WARP_NOTARIZATION_PASSWORD: ${{ secrets.WARP_NOTARIZATION_PASSWORD }} - name: Package CLI binary run: | mv ${{ steps.bundle_cli.outputs.binary_path }} oz-${{ steps.get-config.outputs.channel }} tar czf oz-${{ steps.get-config.outputs.channel }}-macos-${{ matrix.arch }}.tar.gz oz-${{ steps.get-config.outputs.channel }} -C "$(dirname "${{ steps.bundle_cli.outputs.bundled_resources_dir }}")" resources - name: Add CLI to GitHub release assets if: ${{ needs.prepare_release.outputs.should_publish == 'true' }} uses: softprops/action-gh-release@da05d552573ad5aba039eaac05058a918a7bf631 # v2.2.2 with: tag_name: ${{ needs.prepare_release.outputs.release_tag }} files: oz-${{ steps.get-config.outputs.channel }}-macos-${{ matrix.arch }}.tar.gz token: ${{ secrets.GITHUB_TOKEN }} - uses: google-github-actions/auth@7c6bc770dae815cd3e89ee6cdf493a5fab2cc093 # v3.0.0 if: ${{ needs.prepare_release.outputs.should_publish == 'true' }} with: credentials_json: ${{ secrets.GOOGLE_APPLICATION_CREDENTIALS }} - name: Upload CLI to Google Cloud Storage if: ${{ needs.prepare_release.outputs.should_publish == 'true' }} uses: google-github-actions/upload-cloud-storage@e95a15f226403ed658d3e65f40205649f342ba2c # v1 with: path: oz-${{ steps.get-config.outputs.channel }}-macos-${{ matrix.arch }}.tar.gz destination: warp-releases/${{ steps.get-config.outputs.channel }}/${{ needs.prepare_release.outputs.release_tag }}/cli/macos/${{ matrix.arch }} headers: |- cache-control: ${{ steps.get-config.outputs.gcs_cache_control_value }} - name: Upload CLI as workflow artifact if: ${{ needs.prepare_release.outputs.should_publish != 'true' }} uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6 with: name: release-macos-cli-${{ matrix.arch }}-${{ steps.get-config.outputs.channel }} path: oz-${{ steps.get-config.outputs.channel }}-macos-${{ matrix.arch }}.tar.gz - name: Set up Sentry CLI if: ${{ needs.prepare_release.outputs.should_publish == 'true' }} uses: matbour/setup-sentry-cli@3e938c54b3018bdd019973689ef984e033b0454b # v2.0.0 with: token: ${{ secrets.SENTRY_RELEASE_TOKEN }} organization: warpdotdev project: ${{ steps.get-config.outputs.sentry_project }} - name: Upload debugging symbols to Sentry if: ${{ needs.prepare_release.outputs.should_publish == 'true' }} run: | script/sentry_upload_dif.sh shell: bash env: DEBUG_FILE_OR_FOLDER_PATH: ${{ steps.bundle_cli.outputs.dsym_path }} release_linux_x86: name: Build Release (Linux x86_64) runs-on: namespace-profile-ubuntu-20-04 needs: prepare_release if: ${{ inputs.build_linux != false }} timeout-minutes: 60 env: # Automatically extract AppImages before running them instead of mounting # them with FUSE, which isn't available on GitHub runners (and this is # easier and less error-prone than trying to install it). APPIMAGE_EXTRACT_AND_RUN: "1" steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - uses: ./.github/actions/prepare_environment with: target_os: linux is_self_hosted: false ref: ${{ needs.prepare_release.outputs.release_branch }} install_release_deps: true # Install gcc 10, as gcc 9.5 has a bug with memcmp that is incompatible # with aws-lc-rs (see https://gcc.gnu.org/bugzilla/show_bug.cgi?id=95189). - name: Install and configure gcc-10 run: | sudo apt update sudo apt install -y gcc-10 g++-10 sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-10 60 --slave /usr/bin/g++ g++ /usr/bin/g++-10 gcc --version g++ --version - name: Get channel configuration id: get-config uses: ./.github/actions/get_channel_config/ with: config_file: ${{ env.CONFIG_FILE }} channel: ${{ inputs.channel }} - uses: google-github-actions/auth@7c6bc770dae815cd3e89ee6cdf493a5fab2cc093 # v3.0.0 with: credentials_json: ${{ secrets.GOOGLE_APPLICATION_CREDENTIALS }} - name: Install linuxdeploy run: script/linux/install_linuxdeploy - name: Load PGP signing key and passphrase run: | mkdir -p ~/.gnupg echo "default-cache-ttl 7200" >> ~/.gnupg/gpg-agent.conf # Set the appropriate permissions for files and directories. find ~/.gnupg -type f -exec chmod 600 {} \; find ~/.gnupg -type d -exec chmod 700 {} \; # Test the agent configuration. This is helpful for debugging if # something is invalid. gpgconf --check-options gpg-agent gpg-agent --gpgconf-test # Kill the agent, if one is running, and then start a new one - this # ensures our updated configuration is applied correctly. gpgconf --kill gpg-agent gpgconf --launch gpg-agent # Retrieve our signing key and import it into gpg. gcloud --project astral-field-294621 secrets versions access latest --secret=linux-releases-pgp-key | gpg --batch --import # Populate the passphrase for our signing key in the gpg-agent cache. # by signing an empty file. touch emptyfile gpg --no-tty --pinentry-mode loopback --passphrase-fd 0 --detach-sign --armor --batch --yes --output emptyfile.sig emptyfile << EOF ${{ secrets.LINUX_RELEASES_PGP_KEY_PASSPHRASE }} EOF rm emptyfile.sig shell: bash # Namespace's User Bundled Cache persists target/ between jobs on the same profile, which can # leave stale packages from a previous channel's build in the linux bundle output dir. - name: Clean stale bundle output run: rm -rf target/*/bundle/linux shell: bash - name: Bundle app id: bundle_app run: | # Build everything but the arch package, as we need to build it # within a Docker container. script/bundle --channel $CHANNEL --packages appimage,deb,rpm shell: bash env: CHANNEL: ${{ steps.get-config.outputs.channel }} GIT_RELEASE_TAG: ${{ needs.prepare_release.outputs.release_tag }} - name: Bundle Arch Linux package uses: ./.github/actions/bundle_arch_package with: channel: ${{ steps.get-config.outputs.channel }} release-tag: ${{ needs.prepare_release.outputs.release_tag }} arch: x86_64 - name: Sign Arch Linux packages run: | ./script/linux/sign_arch_packages "$PKGDIR" shell: bash env: PKGDIR: ${{ steps.bundle_app.outputs.packages_dir }} - name: Set up Sentry CLI if: ${{ needs.prepare_release.outputs.should_publish == 'true' }} uses: matbour/setup-sentry-cli@3e938c54b3018bdd019973689ef984e033b0454b # v2.0.0 with: token: ${{ secrets.SENTRY_RELEASE_TOKEN }} organization: warpdotdev project: ${{ steps.get-config.outputs.sentry_project }} - name: Upload debugging symbols to Sentry if: ${{ needs.prepare_release.outputs.should_publish == 'true' }} run: | script/sentry_upload_dif.sh shell: bash env: DEBUG_FILE_OR_FOLDER_PATH: ${{ steps.bundle_app.outputs.debug_executable_path }} - name: Add packages to GitHub release assets if: ${{ needs.prepare_release.outputs.should_publish == 'true' }} id: upload_github_assets uses: softprops/action-gh-release@da05d552573ad5aba039eaac05058a918a7bf631 # v2.2.2 with: tag_name: ${{ needs.prepare_release.outputs.release_tag }} files: ${{ steps.bundle_app.outputs.packages_dir }}/* token: ${{ secrets.GITHUB_TOKEN }} - name: Upload packages to Google Cloud Storage if: ${{ needs.prepare_release.outputs.should_publish == 'true' }} uses: google-github-actions/upload-cloud-storage@e95a15f226403ed658d3e65f40205649f342ba2c # v1 with: path: ${{ steps.bundle_app.outputs.packages_dir }} # Only upload the files from within `packages_dir`, don't include the # (parent) folder itself. parent: false destination: warp-releases/${{ steps.get-config.outputs.channel }}/${{ needs.prepare_release.outputs.release_tag }} # IMPORTANT: both the key and value of the cache-control header must be all lowercase; # uppercase values will not be respected by Cloud CDN and using "Cache-Control" will be # ignored by the upload-cloud-storage action. headers: |- cache-control: ${{ steps.get-config.outputs.gcs_cache_control_value }} - name: Upload packages as workflow artifact if: ${{ needs.prepare_release.outputs.should_publish != 'true' }} uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6 with: name: release-linux-x86_64-${{ steps.get-config.outputs.channel }} path: ${{ steps.bundle_app.outputs.packages_dir }} release_linux_cli_x86: name: Build Release (Linux CLI x86_64) runs-on: namespace-profile-ubuntu-20-04 needs: prepare_release if: ${{ inputs.build_linux != false }} timeout-minutes: 60 steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - uses: ./.github/actions/prepare_environment with: target_os: linux is_self_hosted: false ref: ${{ needs.prepare_release.outputs.release_branch }} install_release_deps: true # Install gcc 10, as gcc 9.5 has a bug with memcmp that is incompatible # with aws-lc-rs (see https://gcc.gnu.org/bugzilla/show_bug.cgi?id=95189). - name: Install and configure gcc-10 run: | sudo apt update sudo apt install -y gcc-10 g++-10 sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-10 60 --slave /usr/bin/g++ g++ /usr/bin/g++-10 gcc --version g++ --version - name: Get channel configuration id: get-config uses: ./.github/actions/get_channel_config/ with: config_file: ${{ env.CONFIG_FILE }} channel: ${{ inputs.channel }} - uses: google-github-actions/auth@7c6bc770dae815cd3e89ee6cdf493a5fab2cc093 # v3.0.0 with: credentials_json: ${{ secrets.GOOGLE_APPLICATION_CREDENTIALS }} - name: Load PGP signing key and passphrase run: | mkdir -p ~/.gnupg echo "default-cache-ttl 7200" >> ~/.gnupg/gpg-agent.conf # Set the appropriate permissions for files and directories. find ~/.gnupg -type f -exec chmod 600 {} \; find ~/.gnupg -type d -exec chmod 700 {} \; # Test the agent configuration. This is helpful for debugging if # something is invalid. gpgconf --check-options gpg-agent gpg-agent --gpgconf-test # Kill the agent, if one is running, and then start a new one - this # ensures our updated configuration is applied correctly. gpgconf --kill gpg-agent gpgconf --launch gpg-agent # Retrieve our signing key and import it into gpg. gcloud --project astral-field-294621 secrets versions access latest --secret=linux-releases-pgp-key | gpg --batch --import # Populate the passphrase for our signing key in the gpg-agent cache. # by signing an empty file. touch emptyfile gpg --no-tty --pinentry-mode loopback --passphrase-fd 0 --detach-sign --armor --batch --yes --output emptyfile.sig emptyfile << EOF ${{ secrets.LINUX_RELEASES_PGP_KEY_PASSPHRASE }} EOF rm emptyfile.sig shell: bash # Namespace's User Bundled Cache persists target/ between jobs on the same profile, which can # leave stale packages from a previous channel's build in the linux bundle output dir. - name: Clean stale bundle output run: rm -rf target/*/bundle/linux shell: bash - name: Bundle CLI id: bundle_cli run: | # Build CLI only script/bundle --channel $CHANNEL --artifact cli --packages deb,rpm shell: bash env: CHANNEL: ${{ steps.get-config.outputs.channel }} GIT_RELEASE_TAG: ${{ needs.prepare_release.outputs.release_tag }} - name: Package CLI tar.gz run: | cp ${{ steps.bundle_cli.outputs.executable_path }} oz-${{ steps.get-config.outputs.channel }} tar czf oz-${{ steps.get-config.outputs.channel }}-linux-x86_64.tar.gz oz-${{ steps.get-config.outputs.channel }} -C "$(dirname "${{ steps.bundle_cli.outputs.bundled_resources_dir }}")" resources - name: Bundle Arch Linux CLI package uses: ./.github/actions/bundle_arch_package with: channel: ${{ steps.get-config.outputs.channel }} release-tag: ${{ needs.prepare_release.outputs.release_tag }} arch: x86_64 artifact: cli - name: Sign Arch Linux packages run: | ./script/linux/sign_arch_packages "$PKGDIR" shell: bash env: PKGDIR: ${{ steps.bundle_cli.outputs.packages_dir }} - name: Set up Sentry CLI if: ${{ needs.prepare_release.outputs.should_publish == 'true' }} uses: matbour/setup-sentry-cli@3e938c54b3018bdd019973689ef984e033b0454b # v2.0.0 with: token: ${{ secrets.SENTRY_RELEASE_TOKEN }} organization: warpdotdev project: ${{ steps.get-config.outputs.sentry_project }} - name: Upload debugging symbols to Sentry if: ${{ needs.prepare_release.outputs.should_publish == 'true' }} run: | script/sentry_upload_dif.sh shell: bash env: DEBUG_FILE_OR_FOLDER_PATH: ${{ steps.bundle_cli.outputs.debug_executable_path }} - name: Add CLI packages to GitHub release assets if: ${{ needs.prepare_release.outputs.should_publish == 'true' }} id: upload_github_assets uses: softprops/action-gh-release@da05d552573ad5aba039eaac05058a918a7bf631 # v2.2.2 with: tag_name: ${{ needs.prepare_release.outputs.release_tag }} files: ${{ steps.bundle_cli.outputs.packages_dir }}/* token: ${{ secrets.GITHUB_TOKEN }} - name: Upload CLI packages to Google Cloud Storage if: ${{ needs.prepare_release.outputs.should_publish == 'true' }} uses: google-github-actions/upload-cloud-storage@e95a15f226403ed658d3e65f40205649f342ba2c # v1 with: path: ${{ steps.bundle_cli.outputs.packages_dir }} # Only upload the files from within `packages_dir`, don't include the # (parent) folder itself. parent: false destination: warp-releases/${{ steps.get-config.outputs.channel }}/${{ needs.prepare_release.outputs.release_tag }} # IMPORTANT: both the key and value of the cache-control header must be all lowercase; # uppercase values will not be respected by Cloud CDN and using "Cache-Control" will be # ignored by the upload-cloud-storage action. headers: |- cache-control: ${{ steps.get-config.outputs.gcs_cache_control_value }} - name: Add CLI tar.gz to GitHub release assets if: ${{ needs.prepare_release.outputs.should_publish == 'true' }} uses: softprops/action-gh-release@da05d552573ad5aba039eaac05058a918a7bf631 # v2.2.2 with: tag_name: ${{ needs.prepare_release.outputs.release_tag }} files: oz-${{ steps.get-config.outputs.channel }}-linux-x86_64.tar.gz token: ${{ secrets.GITHUB_TOKEN }} - name: Upload CLI tar.gz to Google Cloud Storage if: ${{ needs.prepare_release.outputs.should_publish == 'true' }} uses: google-github-actions/upload-cloud-storage@e95a15f226403ed658d3e65f40205649f342ba2c # v1 with: path: oz-${{ steps.get-config.outputs.channel }}-linux-x86_64.tar.gz destination: warp-releases/${{ steps.get-config.outputs.channel }}/${{ needs.prepare_release.outputs.release_tag }}/cli/linux/x86_64 headers: |- cache-control: ${{ steps.get-config.outputs.gcs_cache_control_value }} - name: Upload CLI packages as workflow artifact if: ${{ needs.prepare_release.outputs.should_publish != 'true' }} uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6 with: name: release-linux-cli-x86_64-${{ steps.get-config.outputs.channel }} path: | ${{ steps.bundle_cli.outputs.packages_dir }} oz-${{ steps.get-config.outputs.channel }}-linux-x86_64.tar.gz build_linux_arm_binaries: name: Build Release (Linux ARM) runs-on: namespace-profile-ubuntu-20-04-arm needs: prepare_release if: ${{ inputs.build_linux != false }} timeout-minutes: 60 env: # Automatically extract AppImages before running them instead of mounting # them with FUSE, which isn't available on GitHub runners (and this is # easier and less error-prone than trying to install it). APPIMAGE_EXTRACT_AND_RUN: "1" steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - uses: ./.github/actions/prepare_environment with: target_os: linux is_self_hosted: false ref: ${{ needs.prepare_release.outputs.release_branch }} install_release_deps: true # Install gcc 10, as gcc 9.5 has a bug with memcmp that is incompatible # with aws-lc-rs (see https://gcc.gnu.org/bugzilla/show_bug.cgi?id=95189). - name: Install and configure gcc-10 run: | sudo apt update sudo apt install -y gcc-10 g++-10 sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-10 60 --slave /usr/bin/g++ g++ /usr/bin/g++-10 gcc --version g++ --version - name: Update path run: | # Exports .local/bin into the path for subsequent runs. # We need this for the linuxdeploy install and bundle steps, # as the linuxdeploy appimage is installed to $HOME/local/bin. if [[ ":$PATH:" != *":$HOME/.local/bin:"* ]]; then echo "Adding $HOME/.local/bin to PATH" echo "$HOME/.local/bin" >> $GITHUB_PATH fi shell: bash - name: Get channel configuration id: get-config uses: ./.github/actions/get_channel_config/ with: config_file: ${{ env.CONFIG_FILE }} channel: ${{ inputs.channel }} - uses: google-github-actions/auth@7c6bc770dae815cd3e89ee6cdf493a5fab2cc093 # v3.0.0 with: credentials_json: ${{ secrets.GOOGLE_APPLICATION_CREDENTIALS }} - name: Install linuxdeploy run: script/linux/install_linuxdeploy # Namespace's User Bundled Cache persists target/ between jobs on the same profile, which can # leave stale packages from a previous channel's build in the linux bundle output dir. - name: Clean stale bundle output run: rm -rf target/*/bundle/linux shell: bash - name: Build app id: build_app run: | # Build the code, but only bundle the appimage script/bundle --channel $CHANNEL --packages appimage shell: bash env: CHANNEL: ${{ steps.get-config.outputs.channel }} GIT_RELEASE_TAG: ${{ needs.prepare_release.outputs.release_tag }} - name: Package binaries id: package_binaries run: | # Tar up all the binaries we need for the next stage EXECUTABLE_PATH="$(realpath --relative-to=$(pwd) $EXECUTABLE_PATH)" DEBUG_EXECUTABLE_PATH="$(realpath --relative-to=$(pwd) $DEBUG_EXECUTABLE_PATH)" PACKAGES_DIR="$(realpath --relative-to=$(pwd) $PACKAGES_DIR)" tar czvf binaries.tar.gz $EXECUTABLE_PATH $DEBUG_EXECUTABLE_PATH $PACKAGES_DIR shell: bash env: EXECUTABLE_PATH: ${{ steps.build_app.outputs.executable_path }} DEBUG_EXECUTABLE_PATH: ${{ steps.build_app.outputs.debug_executable_path }} PACKAGES_DIR: ${{steps.build_app.outputs.packages_dir}} - name: Archive build files uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6 with: name: built-arm64-linux-${{steps.get-config.outputs.channel}} retention-days: 1 path: binaries.tar.gz build_linux_cli_arm_binaries: name: Build Release (Linux CLI ARM) runs-on: namespace-profile-ubuntu-20-04-arm needs: prepare_release if: ${{ inputs.build_linux != false }} timeout-minutes: 60 steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - uses: ./.github/actions/prepare_environment with: target_os: linux is_self_hosted: false ref: ${{ needs.prepare_release.outputs.release_branch }} install_release_deps: true # Install gcc 10, as gcc 9.5 has a bug with memcmp that is incompatible # with aws-lc-rs (see https://gcc.gnu.org/bugzilla/show_bug.cgi?id=95189). - name: Install and configure gcc-10 run: | sudo apt update sudo apt install -y gcc-10 g++-10 sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-10 60 --slave /usr/bin/g++ g++ /usr/bin/g++-10 gcc --version g++ --version - name: Get channel configuration id: get-config uses: ./.github/actions/get_channel_config/ with: config_file: ${{ env.CONFIG_FILE }} channel: ${{ inputs.channel }} - uses: google-github-actions/auth@7c6bc770dae815cd3e89ee6cdf493a5fab2cc093 # v3.0.0 with: credentials_json: ${{ secrets.GOOGLE_APPLICATION_CREDENTIALS }} # Namespace's User Bundled Cache persists target/ between jobs on the same profile, which can # leave stale packages from a previous channel's build in the linux bundle output dir. - name: Clean stale bundle output run: rm -rf target/*/bundle/linux shell: bash - name: Build CLI id: build_cli run: | # Build the CLI only script/bundle --channel $CHANNEL --artifact cli --packages none shell: bash env: CHANNEL: ${{ steps.get-config.outputs.channel }} GIT_RELEASE_TAG: ${{ needs.prepare_release.outputs.release_tag }} - name: Package binaries id: package_binaries run: | # Tar up all the binaries we need for the next stage EXECUTABLE_PATH="$(realpath --relative-to=$(pwd) $EXECUTABLE_PATH)" DEBUG_EXECUTABLE_PATH="$(realpath --relative-to=$(pwd) $DEBUG_EXECUTABLE_PATH)" PACKAGES_DIR="$(realpath --relative-to=$(pwd) $PACKAGES_DIR)" tar czvf binaries-cli.tar.gz $EXECUTABLE_PATH $DEBUG_EXECUTABLE_PATH $PACKAGES_DIR shell: bash env: EXECUTABLE_PATH: ${{ steps.build_cli.outputs.executable_path }} DEBUG_EXECUTABLE_PATH: ${{ steps.build_cli.outputs.debug_executable_path }} PACKAGES_DIR: ${{steps.build_cli.outputs.packages_dir}} - name: Archive build files uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6 with: name: built-arm64-linux-cli-${{steps.get-config.outputs.channel}} retention-days: 1 path: binaries-cli.tar.gz # This is done as a separate job from `build_linux_arm_binaries` and `build_linux_cli_arm_binaries` # because the steps here were hard to get running on the self-hosted runners. release_linux_arm: name: Bundle Release (Linux ARM) runs-on: namespace-profile-ubuntu-20-04 needs: [ prepare_release, build_linux_arm_binaries, build_linux_cli_arm_binaries ] if: ${{ inputs.build_linux != false }} timeout-minutes: 60 steps: - name: Checkout sources uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: ref: ${{ needs.prepare_release.outputs.release_branch }} - name: Get channel configuration id: get-config uses: ./.github/actions/get_channel_config/ with: config_file: ${{ env.CONFIG_FILE }} channel: ${{ inputs.channel }} - uses: google-github-actions/auth@7c6bc770dae815cd3e89ee6cdf493a5fab2cc093 # v3.0.0 with: credentials_json: ${{ secrets.GOOGLE_APPLICATION_CREDENTIALS }} - name: Set up Sentry CLI if: ${{ needs.prepare_release.outputs.should_publish == 'true' }} uses: matbour/setup-sentry-cli@3e938c54b3018bdd019973689ef984e033b0454b # v2.0.0 with: token: ${{ secrets.SENTRY_RELEASE_TOKEN }} organization: warpdotdev project: ${{ steps.get-config.outputs.sentry_project }} # Install gcc 10, as gcc 9.5 has a bug with memcmp that is incompatible # with aws-lc-rs (see https://gcc.gnu.org/bugzilla/show_bug.cgi?id=95189). - name: Install and configure gcc-10 run: | sudo apt update sudo apt install -y gcc-10 g++-10 sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-10 60 --slave /usr/bin/g++ g++ /usr/bin/g++-10 gcc --version g++ --version # Install Linux build dependencies (protoc, libssl-dev, etc.). Although this job # runs with --skip-build, script/prepare_bundled_resources still invokes # `cargo run --bin generate_settings_schema` and `cargo about generate`, which # compile workspace crates whose build scripts depend on protoc and other # system libraries. The four sibling Linux jobs get these via prepare_environment; # this job doesn't use prepare_environment, so install them explicitly here. - name: Install Linux build dependencies shell: bash run: ./script/linux/install_build_deps - name: Install release dependencies shell: bash run: | ./script/install_cargo_release_deps --no-build-deps - name: Load PGP signing key and passphrase run: | mkdir -p ~/.gnupg echo "default-cache-ttl 7200" >> ~/.gnupg/gpg-agent.conf # Set the appropriate permissions for files and directories. find ~/.gnupg -type f -exec chmod 600 {} \; find ~/.gnupg -type d -exec chmod 700 {} \; # Test the agent configuration. This is helpful for debugging if # something is invalid. gpgconf --check-options gpg-agent gpg-agent --gpgconf-test # Kill the agent, if one is running, and then start a new one - this # ensures our updated configuration is applied correctly. gpgconf --kill gpg-agent gpgconf --launch gpg-agent # Retrieve our signing key and import it into gpg. gcloud --project astral-field-294621 secrets versions access latest --secret=linux-releases-pgp-key | gpg --batch --import # Populate the passphrase for our signing key in the gpg-agent cache. # by signing an empty file. touch emptyfile gpg --no-tty --pinentry-mode loopback --passphrase-fd 0 --detach-sign --armor --batch --yes --output emptyfile.sig emptyfile << EOF ${{ secrets.LINUX_RELEASES_PGP_KEY_PASSPHRASE }} EOF rm emptyfile.sig echo "Populated gpg-agent cache" shell: bash # Bundle the app packages # Namespace's User Bundled Cache persists target/ between jobs on the same profile, which can # leave stale packages from a previous channel's build in the linux bundle output dir. - name: Clean stale bundle output run: rm -rf target/*/bundle/linux shell: bash - name: Download app build artifacts uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: built-arm64-linux-${{steps.get-config.outputs.channel}} - name: Unpackage app binaries id: unpackage_app_binaries run: | tar xzvf binaries.tar.gz shell: bash - name: Bundle deb/rpm app packages id: bundle_app run: | # Bundle everything but the arch package, as we need to bundle it # within a Docker container. script/bundle --channel $CHANNEL --skip-build --packages deb,rpm --arch aarch64 shell: bash env: CHANNEL: ${{ steps.get-config.outputs.channel }} GIT_RELEASE_TAG: ${{ needs.prepare_release.outputs.release_tag }} - name: Bundle Arch Linux app package uses: ./.github/actions/bundle_arch_package with: channel: ${{ steps.get-config.outputs.channel }} release-tag: ${{ needs.prepare_release.outputs.release_tag }} arch: aarch64 - name: Upload app debugging symbols to Sentry if: ${{ needs.prepare_release.outputs.should_publish == 'true' }} # Important: we have to do this before the binary is overwritten by the CLI version, which has the same filename run: | script/sentry_upload_dif.sh env: DEBUG_FILE_OR_FOLDER_PATH: ${{ steps.bundle_app.outputs.debug_executable_path }} shell: bash # Bundle the CLI packages - name: Download CLI build artifacts uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: built-arm64-linux-cli-${{steps.get-config.outputs.channel}} - name: Unpackage CLI binaries id: unpackage_cli_binaries run: | tar xzvf binaries-cli.tar.gz shell: bash - name: Bundle deb/rpm CLI packages id: bundle_cli run: | script/bundle --channel $CHANNEL --skip-build --arch aarch64 --packages deb,rpm --artifact cli shell: bash env: CHANNEL: ${{ steps.get-config.outputs.channel }} GIT_RELEASE_TAG: ${{ needs.prepare_release.outputs.release_tag }} - name: Package CLI tar.gz run: | cp ${{ steps.bundle_cli.outputs.executable_path }} oz-${{ steps.get-config.outputs.channel }} tar czf oz-${{ steps.get-config.outputs.channel }}-linux-aarch64.tar.gz oz-${{ steps.get-config.outputs.channel }} -C "$(dirname "${{ steps.bundle_cli.outputs.bundled_resources_dir }}")" resources - name: Bundle Arch Linux CLI package uses: ./.github/actions/bundle_arch_package with: channel: ${{ steps.get-config.outputs.channel }} release-tag: ${{ needs.prepare_release.outputs.release_tag }} arch: aarch64 artifact: cli - name: Upload CLI debugging symbols to Sentry if: ${{ needs.prepare_release.outputs.should_publish == 'true' }} run: | script/sentry_upload_dif.sh env: DEBUG_FILE_OR_FOLDER_PATH: ${{ steps.bundle_cli.outputs.debug_executable_path }} shell: bash # Common uploading steps - name: Sign Arch Linux packages run: | # We technically only need to run `sign_arch_packages` once, as $APP_PKGDIR and $CLI_PKGDIR should be the same. ./script/linux/sign_arch_packages "$APP_PKGDIR" ./script/linux/sign_arch_packages "$CLI_PKGDIR" shell: bash env: APP_PKGDIR: ${{ steps.bundle_app.outputs.packages_dir }} CLI_PKGDIR: ${{ steps.bundle_cli.outputs.packages_dir }} - name: Add app packages to GitHub release assets if: ${{ needs.prepare_release.outputs.should_publish == 'true' }} uses: softprops/action-gh-release@da05d552573ad5aba039eaac05058a918a7bf631 # v2.2.2 with: tag_name: ${{ needs.prepare_release.outputs.release_tag }} files: ${{ steps.bundle_app.outputs.packages_dir }}/* token: ${{ secrets.GITHUB_TOKEN }} - name: Add CLI packages to GitHub release assets uses: softprops/action-gh-release@da05d552573ad5aba039eaac05058a918a7bf631 # v2.2.2 if: ${{ needs.prepare_release.outputs.should_publish == 'true' && steps.bundle_cli.outputs.packages_dir != steps.bundle_app.outputs.packages_dir }} with: tag_name: ${{ needs.prepare_release.outputs.release_tag }} files: ${{ steps.bundle_cli.outputs.packages_dir }}/* token: ${{ secrets.GITHUB_TOKEN }} - name: Add CLI tar.gz to GitHub release assets if: ${{ needs.prepare_release.outputs.should_publish == 'true' }} uses: softprops/action-gh-release@da05d552573ad5aba039eaac05058a918a7bf631 # v2.2.2 with: tag_name: ${{ needs.prepare_release.outputs.release_tag }} files: oz-${{ steps.get-config.outputs.channel }}-linux-aarch64.tar.gz token: ${{ secrets.GITHUB_TOKEN }} - name: Upload app packages to Google Cloud Storage if: ${{ needs.prepare_release.outputs.should_publish == 'true' }} uses: google-github-actions/upload-cloud-storage@e95a15f226403ed658d3e65f40205649f342ba2c # v1 with: path: ${{ steps.bundle_app.outputs.packages_dir }} parent: false destination: warp-releases/${{ steps.get-config.outputs.channel }}/${{ needs.prepare_release.outputs.release_tag }} # IMPORTANT: both the key and value of the cache-control header must be all lowercase; # uppercase values will not be respected by Cloud CDN and using "Cache-Control" will be # ignored by the upload-cloud-storage action. headers: |- cache-control: ${{ steps.get-config.outputs.gcs_cache_control_value }} - name: Upload CLI packages to Google Cloud Storage uses: google-github-actions/upload-cloud-storage@e95a15f226403ed658d3e65f40205649f342ba2c # v1 if: ${{ needs.prepare_release.outputs.should_publish == 'true' && steps.bundle_cli.outputs.packages_dir != steps.bundle_app.outputs.packages_dir }} with: path: ${{ steps.bundle_cli.outputs.packages_dir }} parent: false destination: warp-releases/${{ steps.get-config.outputs.channel }}/${{ needs.prepare_release.outputs.release_tag }} # IMPORTANT: both the key and value of the cache-control header must be all lowercase; # uppercase values will not be respected by Cloud CDN and using "Cache-Control" will be # ignored by the upload-cloud-storage action. headers: |- cache-control: ${{ steps.get-config.outputs.gcs_cache_control_value }} - name: Upload CLI tar.gz to Google Cloud Storage if: ${{ needs.prepare_release.outputs.should_publish == 'true' }} uses: google-github-actions/upload-cloud-storage@e95a15f226403ed658d3e65f40205649f342ba2c # v1 with: path: oz-${{ steps.get-config.outputs.channel }}-linux-aarch64.tar.gz destination: warp-releases/${{ steps.get-config.outputs.channel }}/${{ needs.prepare_release.outputs.release_tag }}/cli/linux/aarch64 headers: |- cache-control: ${{ steps.get-config.outputs.gcs_cache_control_value }} - name: Upload packages as workflow artifact if: ${{ needs.prepare_release.outputs.should_publish != 'true' }} uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6 with: name: release-linux-arm64-${{ steps.get-config.outputs.channel }} path: | ${{ steps.bundle_app.outputs.packages_dir }} oz-${{ steps.get-config.outputs.channel }}-linux-aarch64.tar.gz release_web: name: Build Release (Web) runs-on: namespace-profile-ubuntu-22-04 needs: prepare_release if: ${{ inputs.build_web != false }} timeout-minutes: 60 steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - uses: ./.github/actions/prepare_environment with: target_os: wasm is_self_hosted: false ref: ${{ needs.prepare_release.outputs.release_branch }} - name: Get channel configuration id: get-config uses: ./.github/actions/get_channel_config/ with: config_file: ${{ env.CONFIG_FILE }} channel: ${{ inputs.channel }} - name: Bundle app id: bundle_app run: | script/wasm/bundle --channel $CHANNEL shell: bash env: CHANNEL: ${{ steps.get-config.outputs.channel }} GIT_RELEASE_TAG: ${{ needs.prepare_release.outputs.release_tag }} - name: Brotli compress app bundle run: | # The brotli binary won't let us compress in-place, so we need to rename back to the # original afterwards. brotli --rm "${{ steps.bundle_app.outputs.packages_dir }}"/* for file in "${{ steps.bundle_app.outputs.packages_dir }}"/*.br; do mv -- "$file" "${file%.br}" done shell: bash - name: Set up Sentry CLI if: ${{ needs.prepare_release.outputs.should_publish == 'true' }} uses: matbour/setup-sentry-cli@3e938c54b3018bdd019973689ef984e033b0454b # v2.0.0 with: token: ${{ secrets.SENTRY_RELEASE_TOKEN }} organization: warpdotdev project: ${{ steps.get-config.outputs.sentry_project }} - name: Upload debugging symbols to Sentry if: ${{ needs.prepare_release.outputs.should_publish == 'true' }} run: | script/sentry_upload_dif.sh shell: bash env: DEBUG_FILE_OR_FOLDER_PATH: ${{ steps.bundle_app.outputs.debug_executable_path }} - name: Add packages to GitHub release assets if: ${{ needs.prepare_release.outputs.should_publish == 'true' }} id: upload_github_assets uses: softprops/action-gh-release@da05d552573ad5aba039eaac05058a918a7bf631 # v2.2.2 with: tag_name: ${{ needs.prepare_release.outputs.release_tag }} files: ${{ steps.bundle_app.outputs.packages_dir }}/* token: ${{ secrets.GITHUB_TOKEN }} - uses: google-github-actions/auth@7c6bc770dae815cd3e89ee6cdf493a5fab2cc093 # v3.0.0 if: ${{ needs.prepare_release.outputs.should_publish == 'true' }} with: credentials_json: ${{ secrets.GOOGLE_APPLICATION_CREDENTIALS }} - name: Upload packages to Google Cloud Storage if: ${{ needs.prepare_release.outputs.should_publish == 'true' }} uses: google-github-actions/upload-cloud-storage@e95a15f226403ed658d3e65f40205649f342ba2c # v1 with: path: ${{ steps.bundle_app.outputs.packages_dir }} # Only upload the files from within `packages_dir`, don't include the # (parent) folder itself. gzip: false parent: false destination: ${{ steps.get-config.outputs.web_gcs_bucket_prefix }}-releases/${{ steps.get-config.outputs.channel }}/${{ needs.prepare_release.outputs.release_tag }} # IMPORTANT: both the key and value of the cache-control header must be all lowercase; # uppercase values will not be respected by Cloud CDN and using "Cache-Control" will be # ignored by the upload-cloud-storage action. headers: |- cache-control: ${{ steps.get-config.outputs.gcs_cache_control_value }} content-encoding: br - name: Upload assets to Google Cloud Storage if: ${{ needs.prepare_release.outputs.should_publish == 'true' }} uses: google-github-actions/upload-cloud-storage@e95a15f226403ed658d3e65f40205649f342ba2c # v1 with: path: ${{ steps.bundle_app.outputs.assets_dir }} parent: false # All asset filenames contain the file hash for cashing purposes, so we store them all in # one directory so they can be cached across releases. destination: ${{ steps.get-config.outputs.web_gcs_bucket_prefix }}-static-assets/ # IMPORTANT: both the key and value of the cache-control header must be all lowercase; # uppercase values will not be respected by Cloud CDN and using "Cache-Control" will be # ignored by the upload-cloud-storage action. # These assets are all stored under cache busting names, so we want to cache them # effectively forever. There's no defined maximum for the max-age directive, but a similar # directive suggests a maximum of one year, so that's what's chosen here. headers: |- cache-control: private, max-age=31536000, immutable - name: Upload web bundle as workflow artifact if: ${{ needs.prepare_release.outputs.should_publish != 'true' }} uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6 with: name: release-web-${{ steps.get-config.outputs.channel }} path: ${{ steps.bundle_app.outputs.packages_dir }} release_windows: name: Build Release (Windows ${{ matrix.arch }}) runs-on: ${{ matrix.runner }} needs: prepare_release if: ${{ inputs.build_windows != false }} timeout-minutes: 150 strategy: matrix: include: - arch: x64 runner: windows-latest-large - arch: arm64 runner: windows-latest-large env: TRUSTED_SIGNING_ENDPOINT: https://eus.codesigning.azure.net/ TRUSTED_SIGNING_ACCOUNT: warpdotdev TRUSTED_SIGNING_CERT_PROFILE: warpterminal steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - uses: ./.github/actions/prepare_environment with: target_os: windows cache_key: ${{ matrix.arch }} is_self_hosted: false ref: ${{ needs.prepare_release.outputs.release_branch }} install_release_deps: true - name: Add arm64 target if: ${{ matrix.arch == 'arm64' }} run: rustup target add aarch64-pc-windows-msvc shell: bash - name: Get channel configuration id: get-config uses: ./.github/actions/get_channel_config/ with: config_file: ${{ env.CONFIG_FILE }} channel: ${{ inputs.channel }} - name: Build binary id: build_binary run: script/bundle -Channel $CHANNEL -skip_build_installer --arch ${{ matrix.arch }} shell: bash env: CHANNEL: ${{ steps.get-config.outputs.channel }} GIT_RELEASE_TAG: ${{ needs.prepare_release.outputs.release_tag }} - name: Sign the executables with Azure Trusted Signing uses: azure/artifact-signing-action@b443cf8ea4124818d2ea9f043cba29fc3ec47b16 # v1.2.0 with: azure-tenant-id: ${{ secrets.AZURE_TENANT_ID }} azure-client-id: ${{ secrets.AZURE_CLIENT_ID }} azure-client-secret: ${{ secrets.AZURE_CLIENT_SECRET }} endpoint: ${{ env.TRUSTED_SIGNING_ENDPOINT }} trusted-signing-account-name: ${{ env.TRUSTED_SIGNING_ACCOUNT }} certificate-profile-name: ${{ env.TRUSTED_SIGNING_CERT_PROFILE }} files: | ${{ steps.build_binary.outputs.binary_path }} ${{ github.workspace }}\app\assets\bundled\bootstrap\pwsh.ps1 files-folder: ${{ github.workspace }}\app\assets\windows\${{ matrix.arch }} files-folder-filter: dll,exe files-folder-recurse: true files-folder-depth: 2 file-digest: SHA256 timestamp-rfc3161: http://timestamp.acs.microsoft.com timestamp-digest: SHA256 # The azure/artifact-signing-action above downloads signtool.exe and the Trusted Signing dlib. # We locate them so ISCC can call signtool during compilation to sign the inner setup engine # (the file extracted to %TEMP% at runtime). This prevents Defender ASR rule D4F940AB from # blocking the installer in enterprise environments. See this issue: # https://github.com/warpdotdev/Warp/issues/8794 - name: Build sign-tool command for Inno Setup id: setup_signing shell: pwsh run: | $Dlib = (Get-ChildItem -Recurse "$env:LOCALAPPDATA" -Filter "Azure.CodeSigning.Dlib.dll" -ErrorAction SilentlyContinue | Where-Object { $_.FullName -match 'x64' } | Select-Object -First 1).FullName if (-not $Dlib) { throw "Azure.CodeSigning.Dlib.dll not found — did the earlier signing action run?" } Write-Output "Dlib: $Dlib" $SignTool = (Get-ChildItem -Recurse "C:\Program Files (x86)\Windows Kits" -Filter "signtool.exe" | Where-Object { $_.FullName -match 'x64' } | Sort-Object FullName -Descending | Select-Object -First 1).FullName if (-not $SignTool) { throw "signtool.exe not found in Windows SDK" } Write-Output "SignTool: $SignTool" # Add signtool's directory to PATH so the ISCC sign command can reference it as just "signtool.exe". # Embedded quotes in the /Scodesign= value break PowerShell's native-command argument passing to ISCC. (Split-Path $SignTool) >> $env:GITHUB_PATH $MetadataPath = "${{ runner.temp }}\tsc-metadata.json" @{Endpoint=$env:TRUSTED_SIGNING_ENDPOINT;CodeSigningAccountName=$env:TRUSTED_SIGNING_ACCOUNT;CertificateProfileName=$env:TRUSTED_SIGNING_CERT_PROFILE} | ConvertTo-Json | Set-Content $MetadataPath "sign_tool_cmd=signtool.exe sign /v /fd SHA256 /tr http://timestamp.acs.microsoft.com /td SHA256 /dlib $Dlib /dmdf $MetadataPath `$f" >> $env:GITHUB_OUTPUT - name: Bundle app id: bundle_app run: script/bundle -Channel $CHANNEL -skip_build_binary --arch ${{ matrix.arch }} shell: bash env: CHANNEL: ${{ steps.get-config.outputs.channel }} GIT_RELEASE_TAG: ${{ needs.prepare_release.outputs.release_tag }} AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} SIGN_TOOL_CMD: ${{ steps.setup_signing.outputs.sign_tool_cmd }} - name: Set up Sentry CLI if: ${{ needs.prepare_release.outputs.should_publish == 'true' }} uses: matbour/setup-sentry-cli@3e938c54b3018bdd019973689ef984e033b0454b # v2.0.0 with: token: ${{ secrets.SENTRY_RELEASE_TOKEN }} organization: warpdotdev project: ${{ steps.get-config.outputs.sentry_project }} - name: Upload binary to Sentry if: ${{ needs.prepare_release.outputs.should_publish == 'true' }} run: | script/sentry_upload_dif.sh shell: bash env: DEBUG_FILE_OR_FOLDER_PATH: ${{ steps.build_binary.outputs.binary_path }} - name: Upload debugging symbols to Sentry if: ${{ needs.prepare_release.outputs.should_publish == 'true' }} run: | script/sentry_upload_dif.sh shell: bash env: DEBUG_FILE_OR_FOLDER_PATH: ${{ steps.bundle_app.outputs.pdb_file_path }} - name: Add installer to GitHub release assets if: ${{ needs.prepare_release.outputs.should_publish == 'true' }} id: upload_github_assets uses: softprops/action-gh-release@da05d552573ad5aba039eaac05058a918a7bf631 # v2.2.2 with: tag_name: ${{ needs.prepare_release.outputs.release_tag }} files: ${{ steps.bundle_app.outputs.installer_path }} token: ${{ secrets.GITHUB_TOKEN }} - uses: google-github-actions/auth@7c6bc770dae815cd3e89ee6cdf493a5fab2cc093 # v3.0.0 if: ${{ needs.prepare_release.outputs.should_publish == 'true' }} with: credentials_json: ${{ secrets.GOOGLE_APPLICATION_CREDENTIALS }} - name: Upload installer to Google Cloud Storage if: ${{ needs.prepare_release.outputs.should_publish == 'true' }} uses: google-github-actions/upload-cloud-storage@e95a15f226403ed658d3e65f40205649f342ba2c # v1 with: path: ${{ steps.bundle_app.outputs.installer_path }} destination: warp-releases/${{ steps.get-config.outputs.channel }}/${{ needs.prepare_release.outputs.release_tag }} # IMPORTANT: both the key and value of the cache-control header must be all lowercase; # uppercase values will not be respected by Cloud CDN and using "Cache-Control" will be # ignored by the upload-cloud-storage action. headers: |- cache-control: ${{ steps.get-config.outputs.gcs_cache_control_value }} - name: Upload installer as workflow artifact if: ${{ needs.prepare_release.outputs.should_publish != 'true' }} uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6 with: name: release-windows-${{ matrix.arch }}-${{ steps.get-config.outputs.channel }} path: ${{ steps.bundle_app.outputs.installer_path }} send_slack_notif_if_unsuccessful: name: Send Slack notification if any release jobs were unsuccessful runs-on: ubuntu-latest needs: - prepare_release - release_macos_single_arch - release_macos_universal - release_macos_cli - release_linux_x86 - release_linux_arm - release_linux_cli_x86 - release_web - release_windows if: ${{ always() && needs.prepare_release.outputs.should_publish == 'true' }} steps: - name: Figure out which releases failed, if any id: failed_releases shell: bash run: | FAILED=() if [[ ${{ needs.release_macos_single_arch.result }} != 'success' ]]; then FAILED+=("MacOS Single Arch") fi if [[ ${{ needs.release_macos_universal.result }} != 'success' ]]; then FAILED+=("MacOS Universal") fi if [[ ${{ needs.release_macos_cli.result }} != 'success' ]]; then FAILED+=('MacOS CLI') fi if [[ ${{ needs.release_linux_x86.result }} != 'success' ]]; then FAILED+=("Linux x86_64") fi if [[ ${{ needs.release_linux_arm.result }} != 'success' ]]; then FAILED+=("Linux ARM64") fi if [[ ${{ needs.release_linux_cli_x86.result }} != 'success' ]]; then FAILED+=("Linux CLI x86_64") fi if [[ ${{ needs.release_web.result }} != 'success' && ${{ inputs.channel }} != 'preview' ]]; then FAILED+=("WASM") fi if [[ ${{ needs.release_windows.result }} != 'success' ]]; then FAILED+=("Windows") fi if (( ${#FAILED[@]} )); then printf -v FORMATTED "%s, " "${FAILED[@]}" echo "failed=$(echo "{${FORMATTED%, }}")" >> $GITHUB_OUTPUT fi - name: Send Slack notification if unsuccessful uses: slackapi/slack-github-action@410ae57cff5c6b682b106440be0e6c7eb8c98c9d # v1.16.0 if: ${{ steps.failed_releases.outputs.failed != '' }} with: channel-id: "#oncall-client" # Follows https://api.slack.com/reference/surfaces/formatting#mentioning-users. # S06N82RS7FA is the user group ID for @oncall-client-eng. slack-message: "Failed to create ${{ steps.failed_releases.outputs.failed }} release artifacts for ${{ inputs.channel }}. See https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}. cc: " env: SLACK_BOT_TOKEN: ${{ secrets.ACTION_MONITORING_SLACK }} generate_changelogs: name: Generate Changelogs if: ${{ needs.prepare_release.outputs.should_publish == 'true' }} runs-on: ubuntu-latest needs: - prepare_release - release_macos_single_arch - release_macos_universal - release_macos_cli - release_linux_x86 - release_linux_arm - release_linux_cli_x86 - release_web - release_windows steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 name: Checkout sources with: # Fetch history for all tags and branches so we can compare revisions # in order to generate the changelog. fetch-depth: 0 - name: Get channel configuration id: get-config uses: ./.github/actions/get_channel_config/ with: config_file: ${{ env.CONFIG_FILE }} channel: ${{ inputs.channel }} - name: Obtain a GitHub App Installation Access Token id: github_app_auth run: | TOKEN="$(npx obtain-github-app-installation-access-token ci ${{ secrets.GH_APP_CREDENTIALS_TOKEN }})" echo "::add-mask::$TOKEN" echo "token=$TOKEN" >> $GITHUB_OUTPUT - name: Generate changelog uses: warpdotdev/generate-changelog@main id: generate_changelog with: channel: ${{ inputs.channel }} github_auth_token: ${{ steps.github_app_auth.outputs.token }} version: ${{ needs.prepare_release.outputs.release_tag }} - name: Build Slack changelog payload id: build_slack_payload shell: bash env: CHANGELOG_MD: ${{ steps.generate_changelog.outputs.changelog }} RELEASE_TAG: ${{ needs.prepare_release.outputs.release_tag }} run: | # Rename the keys in the Changelog JSON to something more human readable. # This is the changelog that goes to slack so it doesn't need to be extremely filtered. NEW_CHANGELOG=$(echo $CHANGELOG_MD | jq 'with_entries(if .key == "newFeatures" then .key = "New Features" else . end)' | jq 'with_entries(if .key == "improvements" then .key = "Improvements" else . end)' | jq 'with_entries(if .key == "bugFixes" then .key = "Bug Fixes" else . end)' | jq 'with_entries(if .key == "images" then .key = "Image" else . end)') # Generate the full markdown text as individual lines. MARKDOWN=$(echo "$NEW_CHANGELOG" | jq -r 'to_entries[] | select(.value | length > 0) | "*\(.key)*", (.value[] | " \u2022 \(.)")') if [ -z "$MARKDOWN" ]; then echo "has_content=" >> $GITHUB_OUTPUT exit 0 fi echo "has_content=true" >> $GITHUB_OUTPUT # Split markdown into chunks of <= 3000 characters at newline boundaries. # Each chunk will become its own section block in the Slack payload. CHUNKS='[]' BUFFER="" while IFS= read -r line; do if [ -z "$BUFFER" ]; then CANDIDATE="$line" else CANDIDATE="$BUFFER" CANDIDATE+=$'\n' CANDIDATE+="$line" fi if [ ${#CANDIDATE} -gt 3000 ]; then # Flush the current buffer as a chunk (if non-empty), start a new one with this line. if [ -n "$BUFFER" ]; then CHUNKS=$(echo "$CHUNKS" | jq --arg chunk "$BUFFER" '. + [$chunk]') fi BUFFER="$line" else BUFFER="$CANDIDATE" fi done <<< "$MARKDOWN" # Flush the remaining buffer. if [ -n "$BUFFER" ]; then CHUNKS=$(echo "$CHUNKS" | jq --arg chunk "$BUFFER" '. + [$chunk]') fi # Build the full Slack Block Kit payload: header block + one section block per chunk. PAYLOAD=$(echo "$CHUNKS" | jq -c \ --arg version "$RELEASE_TAG" \ '{blocks: ([{type: "header", text: {type: "plain_text", text: ("Changelog for " + $version)}}] + [.[] | {type: "section", text: {type: "mrkdwn", text: .}}])}') echo "payload<> $GITHUB_OUTPUT echo "$PAYLOAD" >> $GITHUB_OUTPUT echo "SLACK_PAYLOAD_EOF" >> $GITHUB_OUTPUT - name: Post to a Slack channel id: slack uses: slackapi/slack-github-action@410ae57cff5c6b682b106440be0e6c7eb8c98c9d # v1.16.0 if: ${{ steps.build_slack_payload.outputs.has_content == 'true' }} # Continue on with the rest of the workflow, even if we fail to send the # changelog info to Slack. (We still want to stick the changelog JSON # in Cloud Storage.) continue-on-error: true with: channel-id: ${{ steps.get-config.outputs.changelog_slack_channel }} payload: ${{ steps.build_slack_payload.outputs.payload }} env: SLACK_BOT_TOKEN: ${{ secrets.SLACK_CHANGELOG_BOT }} - name: Generate changelog.json id: changelog_json env: CHANGELOG: ${{ steps.generate_changelog.outputs.changelog }} run: | # Compute the current date/time in the necessary format. DATE=$(date +%Y-%m-%dT%k:%M:%S%z) # Rename the "newFeatures" and "improvements" keys in the Changelog JSON to something more human readable # and ignore everything else since this is for the in-app changelog. CHANGELOG_SECTIONS=$(echo $CHANGELOG | jq 'with_entries(select(.key == "newFeatures" or .key == "improvements"))' | jq 'with_entries(if .key == "newFeatures" then .key = "New features" else . end)' | jq 'with_entries(if .key == "improvements" then .key = "Improvements" else . end)') # create markdown strings for the New Features and Improvements sections NEW_FEATURES=$(echo $CHANGELOG_SECTIONS | jq 'with_entries(select(.key == "New features"))' | jq -r 'to_entries[] | "* \(.value[])"' | awk -v ORS='\n' '1') IMPROVEMENTS=$(echo $CHANGELOG_SECTIONS | jq 'with_entries(select(.key == "Improvements"))' | jq -r 'to_entries[] | "* \(.value[])"' | awk -v ORS='\n' '1') # Extract the image URL from the list, and use the latest URL even if there are multiple IMAGE=$(echo $CHANGELOG | jq -r '.image | if length > 0 then .[-1] else "" end') # Extract Oz updates as a JSON array OZ_UPDATES=$(echo $CHANGELOG | jq -c '.oz_updates // []') # Tweak the structure of the JSON, add in a top-level date field, and add in the markdown_sections field CHANGELOG=$(echo $CHANGELOG_SECTIONS | jq --arg date "$DATE" --arg new_features "$NEW_FEATURES" --arg improvements "$IMPROVEMENTS" --arg image "$IMAGE" --argjson oz_updates "$OZ_UPDATES" '{"date": $date, "sections": to_entries | map({"title": .key, "items": .value}), "markdown_sections": [{"title": "New features", "markdown": $new_features}, {"title": "Improvements", "markdown": $improvements}, {"title": "Coming soon", "markdown": ""}]} + (if $image != "" then {"image_url": $image} else {} end) + (if ($oz_updates | length) > 0 then {"oz_updates": $oz_updates} else {} end)') echo $CHANGELOG > changelog.json cat changelog.json shell: bash - uses: google-github-actions/auth@7c6bc770dae815cd3e89ee6cdf493a5fab2cc093 # v3.0.0 with: credentials_json: ${{ secrets.GOOGLE_APPLICATION_CREDENTIALS }} - name: Upload changelog.json to Google Cloud Storage uses: google-github-actions/upload-cloud-storage@e95a15f226403ed658d3e65f40205649f342ba2c # v1 with: path: changelog.json destination: warp-releases/${{ steps.get-config.outputs.channel }}/${{ needs.prepare_release.outputs.release_tag }}