Default avatar
npub1g8jq...3zcu
npub1g8jq...3zcu
name: Rust on: push: branches: [ "*" ] pull_request: branches: [ "*" ] env: CARGO_TERM_COLOR: always FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true RUST_LOG: info jobs: build: runs-on: ${{ matrix.os }} strategy: matrix: os: [ubuntu-latest, macos-15-intel, macos-latest, windows-latest] features_args: ["", "--no-default-features", "--features nostr"] steps: - uses: actions/checkout@v4 - name: Build ${{ matrix.features_args }} run: cargo build --workspace --verbose ${{ matrix.features_args }} - name: Run workspace tests ${{ matrix.features_args }} run: | cargo test --workspace ${{ matrix.features_args }} -- --test-threads 1 - name: Run get_file_hash_core tests ${{ matrix.features_args }} shell: bash run: | if [[ "${{ matrix.features_args }}" == "--features nostr" ]]; then cargo test -p get_file_hash_core ${{ matrix.features_args }} -- --test-threads 1 --nocapture else cargo test -p get_file_hash_core ${{ matrix.features_args }} -- --test-threads 1 fi - name: Run get_file_hash tests ${{ matrix.features_args }} shell: bash run: | if [[ "${{ matrix.features_args }}" == "--features nostr" ]]; then cargo test -p get_file_hash ${{ matrix.features_args }} -- --test-threads 1 --nocapture else cargo test -p get_file_hash ${{ matrix.features_args }} -- --test-threads 1 fi - name: Build Release ${{ matrix.features_args }} run: cargo build --workspace --release ${{ matrix.features_args }}
# This file was autogenerated by dist: # # Copyright 2022-2024, axodotdev # SPDX-License-Identifier: MIT or Apache-2.0 # # CI that: # # * checks for a Git Tag that looks like a release # * builds artifacts with dist (archives, installers, hashes) # * uploads those artifacts to temporary workflow zip # * on success, uploads the artifacts to a GitHub Release # # Note that the GitHub Release will be created with a generated # title/body based on your changelogs. name: Release permissions: "contents": "write" # This task will run whenever you push a git tag that looks like a version # like "1.0.0", "v0.1.0-prerelease.1", "my-app/0.1.0", "releases/v1.0.0", etc. # Various formats will be parsed into a VERSION and an optional PACKAGE_NAME, where # PACKAGE_NAME must be the name of a Cargo package in your workspace, and VERSION # must be a Cargo-style SemVer Version (must have at least major.minor.patch). # # If PACKAGE_NAME is specified, then the announcement will be for that # package (erroring out if it doesn't have the given version or isn't dist-able). # # If PACKAGE_NAME isn't specified, then the announcement will be for all # (dist-able) packages in the workspace with that version (this mode is # intended for workspaces with only one dist-able package, or with all dist-able # packages versioned/released in lockstep). # # If you push multiple tags at once, separate instances of this workflow will # spin up, creating an independent announcement for each one. However, GitHub # will hard limit this to 3 tags per commit, as it will assume more tags is a # mistake. # # If there's a prerelease-style suffix to the version, then the release(s) # will be marked as a prerelease. on: push: tags: - "**[0-9]+.[0-9]+.[0-9]+*" jobs: # Run 'dist plan' (or host) to determine what tasks we need to do plan: runs-on: "ubuntu-22.04" outputs: val: ${{ steps.plan.outputs.manifest }} tag: ${{ !github.event.pull_request && github.ref_name || '' }} tag-flag: ${{ !github.event.pull_request && format('--tag={0}', github.ref_name) || '' }} publishing: ${{ !github.event.pull_request }} env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} steps: - uses: actions/checkout@v4 with: persist-credentials: false submodules: recursive #- name: Install sccache # run: | # cargo install sccache --version 0.4.0 --locked # echo "$HOME/.cargo/bin" >> $GITHUB_PATH - name: Install dist # we specify bash to get pipefail; it guards against the `curl` command # failing. otherwise `sh` won't catch that `curl` returned non-0 shell: bash run: "curl --proto '=https' --tlsv1.2 -LsSf https://github.com/axodotdev/cargo-dist/releases/download/v0.30.3/cargo-dist-installer.sh | sh" - name: Cache dist uses: actions/upload-artifact@v4 with: name: cargo-dist-cache path: ~/.cargo/bin/dist # sure would be cool if github gave us proper conditionals... # so here's a doubly-nested ternary-via-truthiness to try to provide the best possible # functionality based on whether this is a pull_request, and whether it's from a fork. # (PRs run on the *source* but secrets are usually on the *target* -- that's *good* # but also really annoying to build CI around when it needs secrets to work right.) - id: plan run: | dist ${{ (!github.event.pull_request && format('host --steps=create --tag={0}', github.ref_name)) || 'plan' }} --output-format=json > plan-dist-manifest.json echo "dist ran successfully" cat plan-dist-manifest.json echo "manifest=$(jq -c "." plan-dist-manifest.json)" >> "$GITHUB_OUTPUT" - name: "Upload dist-manifest.json" uses: actions/upload-artifact@v4 with: name: artifacts-plan-dist-manifest path: plan-dist-manifest.json # Build and packages all the platform-specific things build-local-artifacts: name: build-local-artifacts (${{ join(matrix.targets, ', ') }}) # Let the initial task tell us to not run (currently very blunt) needs: - plan if: ${{ fromJson(needs.plan.outputs.val).ci.github.artifacts_matrix.include != null && (needs.plan.outputs.publishing == 'true' || fromJson(needs.plan.outputs.val).ci.github.pr_run_mode == 'upload') }} strategy: fail-fast: false # Target platforms/runners are computed by dist in create-release. # Each member of the matrix has the following arguments: # # - runner: the github runner # - dist-args: cli flags to pass to dist # - install-dist: expression to run to install dist on the runner # # Typically there will be: # - 1 "global" task that builds universal installers # - N "local" tasks that build each platform's binaries and platform-specific installers matrix: ${{ fromJson(needs.plan.outputs.val).ci.github.artifacts_matrix }} runs-on: ${{ matrix.runner }} container: ${{ matrix.container && matrix.container.image || null }} env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} BUILD_MANIFEST_NAME: target/distrib/${{ join(matrix.targets, '-') }}-dist-manifest.json steps: - name: enable windows longpaths run: | git config --global core.longpaths true - uses: actions/checkout@v4 with: persist-credentials: false submodules: recursive #- name: Install sccache # run: | # cargo install sccache --version 0.4.0 --locked # echo "$HOME/.cargo/bin" >> $GITHUB_PATH - name: Install Rust non-interactively if not already installed if: ${{ matrix.container }} run: | if ! command -v cargo > /dev/null 2>&1; then curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y echo "$HOME/.cargo/bin" >> $GITHUB_PATH fi - name: Install dist run: ${{ matrix.install_dist.run }} # Get the dist-manifest - name: Fetch local artifacts uses: actions/download-artifact@v4 with: pattern: artifacts-* path: target/distrib/ merge-multiple: true - name: Install dependencies run: | ${{ matrix.packages_install }} - name: Build artifacts run: | # Actually do builds and make zips and whatnot dist build ${{ needs.plan.outputs.tag-flag }} --print=linkage --output-format=json ${{ matrix.dist_args }} > dist-manifest.json echo "dist ran successfully" - id: cargo-dist name: Post-build # We force bash here just because github makes it really hard to get values up # to "real" actions without writing to env-vars, and writing to env-vars has # inconsistent syntax between shell and powershell. shell: bash run: | # Parse out what we just built and upload it to scratch storage echo "paths<<EOF" >> "$GITHUB_OUTPUT" dist print-upload-files-from-manifest --manifest dist-manifest.json >> "$GITHUB_OUTPUT" echo "EOF" >> "$GITHUB_OUTPUT" cp dist-manifest.json "$BUILD_MANIFEST_NAME" - name: "Upload artifacts" uses: actions/upload-artifact@v4 with: name: artifacts-build-local-${{ join(matrix.targets, '_') }} path: | ${{ steps.cargo-dist.outputs.paths }} ${{ env.BUILD_MANIFEST_NAME }} # Build and package all the platform-agnostic(ish) things build-global-artifacts: needs: - plan - build-local-artifacts runs-on: "ubuntu-22.04" env: BUILD_MANIFEST_NAME: target/distrib/global-dist-manifest.json steps: - uses: actions/checkout@v4 with: persist-credentials: false submodules: recursive #- name: Install sccache # run: | # cargo install sccache --version 0.4.0 --locked # echo "$HOME/.cargo/bin" >> $GITHUB_PATH - name: Install cached dist uses: actions/download-artifact@v4 with: name: cargo-dist-cache path: ~/.cargo/bin/ - run: chmod +x ~/.cargo/bin/dist # Get all the local artifacts for the global tasks to use (for e.g. checksums) - name: Fetch local artifacts uses: actions/download-artifact@v4 with: pattern: artifacts-* path: target/distrib/ merge-multiple: true - id: cargo-dist shell: bash run: | dist build ${{ needs.plan.outputs.tag-flag }} --output-format=json "--artifacts=global" > dist-manifest.json echo "dist ran successfully" # Parse out what we just built and upload it to scratch storage echo "paths<<EOF" >> "$GITHUB_OUTPUT" jq --raw-output ".upload_files[]" dist-manifest.json >> "$GITHUB_OUTPUT" echo "EOF" >> "$GITHUB_OUTPUT" cp dist-manifest.json "$BUILD_MANIFEST_NAME" - name: "Upload artifacts" uses: actions/upload-artifact@v4 with: name: artifacts-build-global path: | ${{ steps.cargo-dist.outputs.paths }} ${{ env.BUILD_MANIFEST_NAME }} # Determines if we should publish/announce host: needs: - plan - build-local-artifacts - build-global-artifacts # Only run if we're "publishing", and only if plan, local and global didn't fail (skipped is fine) if: ${{ always() && needs.plan.result == 'success' && needs.plan.outputs.publishing == 'true' && (needs.build-global-artifacts.result == 'skipped' || needs.build-global-artifacts.result == 'success') && (needs.build-local-artifacts.result == 'skipped' || needs.build-local-artifacts.result == 'success') }} env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} runs-on: "ubuntu-22.04" outputs: val: ${{ steps.host.outputs.manifest }} steps: - uses: actions/checkout@v4 with: persist-credentials: false submodules: recursive #- name: Install sccache # run: | # cargo install sccache --version 0.4.0 --locked # echo "$HOME/.cargo/bin" >> $GITHUB_PATH - name: Install cached dist uses: actions/download-artifact@v4 with: name: cargo-dist-cache path: ~/.cargo/bin/ - run: chmod +x ~/.cargo/bin/dist # Fetch artifacts from scratch-storage - name: Fetch artifacts uses: actions/download-artifact@v4 with: pattern: artifacts-* path: target/distrib/ merge-multiple: true - id: host shell: bash run: | dist host ${{ needs.plan.outputs.tag-flag }} --steps=upload --steps=release --output-format=json > dist-manifest.json echo "artifacts uploaded and released successfully" cat dist-manifest.json echo "manifest=$(jq -c "." dist-manifest.json)" >> "$GITHUB_OUTPUT" - name: "Upload dist-manifest.json" uses: actions/upload-artifact@v4 with: # Overwrite the previous copy name: artifacts-dist-manifest path: dist-manifest.json # Create a GitHub Release while uploading all files to it - name: "Download GitHub Artifacts" uses: actions/download-artifact@v4 with: pattern: artifacts-* path: artifacts merge-multiple: true - name: Cleanup run: | # Remove the granular manifests rm -f artifacts/*-dist-manifest.json - name: Create GitHub Release env: PRERELEASE_FLAG: "${{ fromJson(steps.host.outputs.manifest).announcement_is_prerelease && '--prerelease' || '' }}" ANNOUNCEMENT_TITLE: "${{ fromJson(steps.host.outputs.manifest).announcement_title }}" ANNOUNCEMENT_BODY: "${{ fromJson(steps.host.outputs.manifest).announcement_github_body }}" RELEASE_COMMIT: "${{ github.sha }}" run: | # Write and read notes from a file to avoid quoting breaking things echo "$ANNOUNCEMENT_BODY" > $RUNNER_TEMP/notes.txt gh release create "${{ needs.plan.outputs.tag }}" --target "$RELEASE_COMMIT" $PRERELEASE_FLAG --title "$ANNOUNCEMENT_TITLE" --notes-file "$RUNNER_TEMP/notes.txt" artifacts/* publish-homebrew-formula: needs: - plan - host runs-on: "ubuntu-22.04" env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} PLAN: ${{ needs.plan.outputs.val }} GITHUB_USER: "axo bot" GITHUB_EMAIL: "admin+bot@axo.dev" if: ${{ !fromJson(needs.plan.outputs.val).announcement_is_prerelease || fromJson(needs.plan.outputs.val).publish_prereleases }} steps: - uses: actions/checkout@v4 with: persist-credentials: true repository: "gnostr-org/homebrew-gnostr" token: ${{ secrets.HOMEBREW_TAP_TOKEN }} # So we have access to the formula - name: Fetch homebrew formulae uses: actions/download-artifact@v4 with: pattern: artifacts-* path: Formula/ merge-multiple: true # This is extra complex because you can make your Formula name not match your app name # so we need to find releases with a *.rb file, and publish with that filename. - name: Commit formula files run: | git config --global user.name "${GITHUB_USER}" git config --global user.email "${GITHUB_EMAIL}" for release in $(echo "$PLAN" | jq --compact-output '.releases[] | select([.artifacts[] | endswith(".rb")] | any)'); do filename=$(echo "$release" | jq '.artifacts[] | select(endswith(".rb"))' --raw-output) name=$(echo "$filename" | sed "s/\.rb$//") version=$(echo "$release" | jq .app_version --raw-output) export PATH="/home/linuxbrew/.linuxbrew/bin:$PATH" brew update # We avoid reformatting user-provided data such as the app description and homepage. brew style --except-cops FormulaAudit/Homepage,FormulaAudit/Desc,FormulaAuditStrict --fix "Formula/${filename}" || true git add "Formula/${filename}" git commit -m "${name} ${version}" done git push origin HEAD announce: needs: - plan - host - publish-homebrew-formula # use "always() && ..." to allow us to wait for all publish jobs while # still allowing individual publish jobs to skip themselves (for prereleases). # "host" however must run to completion, no skipping allowed! if: ${{ always() && needs.host.result == 'success' && (needs.publish-homebrew-formula.result == 'skipped' || needs.publish-homebrew-formula.result == 'success') }} runs-on: "ubuntu-22.04" env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} steps: - uses: actions/checkout@v4 with: persist-credentials: false submodules: recursive
# This file was autogenerated by dist: # # Copyright 2022-2024, axodotdev # SPDX-License-Identifier: MIT or Apache-2.0 # # CI that: # # * checks for a Git Tag that looks like a release # * builds artifacts with dist (archives, installers, hashes) # * uploads those artifacts to temporary workflow zip # * on success, uploads the artifacts to a GitHub Release # # Note that the GitHub Release will be created with a generated # title/body based on your changelogs. name: Release permissions: "contents": "write" # This task will run whenever you push a git tag that looks like a version # like "1.0.0", "v0.1.0-prerelease.1", "my-app/0.1.0", "releases/v1.0.0", etc. # Various formats will be parsed into a VERSION and an optional PACKAGE_NAME, where # PACKAGE_NAME must be the name of a Cargo package in your workspace, and VERSION # must be a Cargo-style SemVer Version (must have at least major.minor.patch). # # If PACKAGE_NAME is specified, then the announcement will be for that # package (erroring out if it doesn't have the given version or isn't dist-able). # # If PACKAGE_NAME isn't specified, then the announcement will be for all # (dist-able) packages in the workspace with that version (this mode is # intended for workspaces with only one dist-able package, or with all dist-able # packages versioned/released in lockstep). # # If you push multiple tags at once, separate instances of this workflow will # spin up, creating an independent announcement for each one. However, GitHub # will hard limit this to 3 tags per commit, as it will assume more tags is a # mistake. # # If there's a prerelease-style suffix to the version, then the release(s) # will be marked as a prerelease. on: push: tags: - "**[0-9]+.[0-9]+.[0-9]+*" jobs: # Run 'dist plan' (or host) to determine what tasks we need to do plan: runs-on: "ubuntu-22.04" outputs: val: ${{ steps.plan.outputs.manifest }} tag: ${{ !github.event.pull_request && github.ref_name || '' }} tag-flag: ${{ !github.event.pull_request && format('--tag={0}', github.ref_name) || '' }} publishing: ${{ !github.event.pull_request }} env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} steps: - uses: actions/checkout@v4 with: persist-credentials: false submodules: recursive #- name: Install sccache # run: | # cargo install sccache --version 0.4.0 --locked # echo "$HOME/.cargo/bin" >> $GITHUB_PATH - name: Install dist # we specify bash to get pipefail; it guards against the `curl` command # failing. otherwise `sh` won't catch that `curl` returned non-0 shell: bash run: "curl --proto '=https' --tlsv1.2 -LsSf https://github.com/axodotdev/cargo-dist/releases/download/v0.30.3/cargo-dist-installer.sh | sh" - name: Cache dist uses: actions/upload-artifact@v4 with: name: cargo-dist-cache path: ~/.cargo/bin/dist # sure would be cool if github gave us proper conditionals... # so here's a doubly-nested ternary-via-truthiness to try to provide the best possible # functionality based on whether this is a pull_request, and whether it's from a fork. # (PRs run on the *source* but secrets are usually on the *target* -- that's *good* # but also really annoying to build CI around when it needs secrets to work right.) - id: plan run: | dist ${{ (!github.event.pull_request && format('host --steps=create --tag={0}', github.ref_name)) || 'plan' }} --output-format=json > plan-dist-manifest.json echo "dist ran successfully" cat plan-dist-manifest.json echo "manifest=$(jq -c "." plan-dist-manifest.json)" >> "$GITHUB_OUTPUT" - name: "Upload dist-manifest.json" uses: actions/upload-artifact@v4 with: name: artifacts-plan-dist-manifest path: plan-dist-manifest.json # Build and packages all the platform-specific things build-local-artifacts: name: build-local-artifacts (${{ join(matrix.targets, ', ') }}) # Let the initial task tell us to not run (currently very blunt) needs: - plan if: ${{ fromJson(needs.plan.outputs.val).ci.github.artifacts_matrix.include != null && (needs.plan.outputs.publishing == 'true' || fromJson(needs.plan.outputs.val).ci.github.pr_run_mode == 'upload') }} strategy: fail-fast: false # Target platforms/runners are computed by dist in create-release. # Each member of the matrix has the following arguments: # # - runner: the github runner # - dist-args: cli flags to pass to dist # - install-dist: expression to run to install dist on the runner # # Typically there will be: # - 1 "global" task that builds universal installers # - N "local" tasks that build each platform's binaries and platform-specific installers matrix: ${{ fromJson(needs.plan.outputs.val).ci.github.artifacts_matrix }} runs-on: ${{ matrix.runner }} container: ${{ matrix.container && matrix.container.image || null }} env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} BUILD_MANIFEST_NAME: target/distrib/${{ join(matrix.targets, '-') }}-dist-manifest.json steps: - name: enable windows longpaths run: | git config --global core.longpaths true - uses: actions/checkout@v4 with: persist-credentials: false submodules: recursive #- name: Install sccache # run: | # cargo install sccache --version 0.4.0 --locked # echo "$HOME/.cargo/bin" >> $GITHUB_PATH - name: Install Rust non-interactively if not already installed if: ${{ matrix.container }} run: | if ! command -v cargo > /dev/null 2>&1; then curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y echo "$HOME/.cargo/bin" >> $GITHUB_PATH fi - name: Install dist run: ${{ matrix.install_dist.run }} # Get the dist-manifest - name: Fetch local artifacts uses: actions/download-artifact@v4 with: pattern: artifacts-* path: target/distrib/ merge-multiple: true - name: Install dependencies run: | ${{ matrix.packages_install }} - name: Build artifacts run: | # Actually do builds and make zips and whatnot dist build ${{ needs.plan.outputs.tag-flag }} --print=linkage --output-format=json ${{ matrix.dist_args }} > dist-manifest.json echo "dist ran successfully" - id: cargo-dist name: Post-build # We force bash here just because github makes it really hard to get values up # to "real" actions without writing to env-vars, and writing to env-vars has # inconsistent syntax between shell and powershell. shell: bash run: | # Parse out what we just built and upload it to scratch storage echo "paths<<EOF" >> "$GITHUB_OUTPUT" dist print-upload-files-from-manifest --manifest dist-manifest.json >> "$GITHUB_OUTPUT" echo "EOF" >> "$GITHUB_OUTPUT" cp dist-manifest.json "$BUILD_MANIFEST_NAME" - name: "Upload artifacts" uses: actions/upload-artifact@v4 with: name: artifacts-build-local-${{ join(matrix.targets, '_') }} path: | ${{ steps.cargo-dist.outputs.paths }} ${{ env.BUILD_MANIFEST_NAME }} # Build and package all the platform-agnostic(ish) things build-global-artifacts: needs: - plan - build-local-artifacts runs-on: "ubuntu-22.04" env: BUILD_MANIFEST_NAME: target/distrib/global-dist-manifest.json steps: - uses: actions/checkout@v4 with: persist-credentials: false submodules: recursive #- name: Install sccache # run: | # cargo install sccache --version 0.4.0 --locked # echo "$HOME/.cargo/bin" >> $GITHUB_PATH - name: Install cached dist uses: actions/download-artifact@v4 with: name: cargo-dist-cache path: ~/.cargo/bin/ - run: chmod +x ~/.cargo/bin/dist # Get all the local artifacts for the global tasks to use (for e.g. checksums) - name: Fetch local artifacts uses: actions/download-artifact@v4 with: pattern: artifacts-* path: target/distrib/ merge-multiple: true - id: cargo-dist shell: bash run: | dist build ${{ needs.plan.outputs.tag-flag }} --output-format=json "--artifacts=global" > dist-manifest.json echo "dist ran successfully" # Parse out what we just built and upload it to scratch storage echo "paths<<EOF" >> "$GITHUB_OUTPUT" jq --raw-output ".upload_files[]" dist-manifest.json >> "$GITHUB_OUTPUT" echo "EOF" >> "$GITHUB_OUTPUT" cp dist-manifest.json "$BUILD_MANIFEST_NAME" - name: "Upload artifacts" uses: actions/upload-artifact@v4 with: name: artifacts-build-global path: | ${{ steps.cargo-dist.outputs.paths }} ${{ env.BUILD_MANIFEST_NAME }} # Determines if we should publish/announce host: needs: - plan - build-local-artifacts - build-global-artifacts # Only run if we're "publishing", and only if plan, local and global didn't fail (skipped is fine) if: ${{ always() && needs.plan.result == 'success' && needs.plan.outputs.publishing == 'true' && (needs.build-global-artifacts.result == 'skipped' || needs.build-global-artifacts.result == 'success') && (needs.build-local-artifacts.result == 'skipped' || needs.build-local-artifacts.result == 'success') }} env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} runs-on: "ubuntu-22.04" outputs: val: ${{ steps.host.outputs.manifest }} steps: - uses: actions/checkout@v4 with: persist-credentials: false submodules: recursive #- name: Install sccache # run: | # cargo install sccache --version 0.4.0 --locked # echo "$HOME/.cargo/bin" >> $GITHUB_PATH - name: Install cached dist uses: actions/download-artifact@v4 with: name: cargo-dist-cache path: ~/.cargo/bin/ - run: chmod +x ~/.cargo/bin/dist # Fetch artifacts from scratch-storage - name: Fetch artifacts uses: actions/download-artifact@v4 with: pattern: artifacts-* path: target/distrib/ merge-multiple: true - id: host shell: bash run: | dist host ${{ needs.plan.outputs.tag-flag }} --steps=upload --steps=release --output-format=json > dist-manifest.json echo "artifacts uploaded and released successfully" cat dist-manifest.json echo "manifest=$(jq -c "." dist-manifest.json)" >> "$GITHUB_OUTPUT" - name: "Upload dist-manifest.json" uses: actions/upload-artifact@v4 with: # Overwrite the previous copy name: artifacts-dist-manifest path: dist-manifest.json # Create a GitHub Release while uploading all files to it - name: "Download GitHub Artifacts" uses: actions/download-artifact@v4 with: pattern: artifacts-* path: artifacts merge-multiple: true - name: Cleanup run: | # Remove the granular manifests rm -f artifacts/*-dist-manifest.json - name: Create GitHub Release env: PRERELEASE_FLAG: "${{ fromJson(steps.host.outputs.manifest).announcement_is_prerelease && '--prerelease' || '' }}" ANNOUNCEMENT_TITLE: "${{ fromJson(steps.host.outputs.manifest).announcement_title }}" ANNOUNCEMENT_BODY: "${{ fromJson(steps.host.outputs.manifest).announcement_github_body }}" RELEASE_COMMIT: "${{ github.sha }}" run: | # Write and read notes from a file to avoid quoting breaking things echo "$ANNOUNCEMENT_BODY" > $RUNNER_TEMP/notes.txt gh release create "${{ needs.plan.outputs.tag }}" --target "$RELEASE_COMMIT" $PRERELEASE_FLAG --title "$ANNOUNCEMENT_TITLE" --notes-file "$RUNNER_TEMP/notes.txt" artifacts/* publish-homebrew-formula: needs: - plan - host runs-on: "ubuntu-22.04" env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} PLAN: ${{ needs.plan.outputs.val }} GITHUB_USER: "axo bot" GITHUB_EMAIL: "admin+bot@axo.dev" if: ${{ !fromJson(needs.plan.outputs.val).announcement_is_prerelease || fromJson(needs.plan.outputs.val).publish_prereleases }} steps: - uses: actions/checkout@v4 with: persist-credentials: true repository: "gnostr-org/homebrew-gnostr" token: ${{ secrets.HOMEBREW_TAP_TOKEN }} # So we have access to the formula - name: Fetch homebrew formulae uses: actions/download-artifact@v4 with: pattern: artifacts-* path: Formula/ merge-multiple: true # This is extra complex because you can make your Formula name not match your app name # so we need to find releases with a *.rb file, and publish with that filename. - name: Commit formula files run: | git config --global user.name "${GITHUB_USER}" git config --global user.email "${GITHUB_EMAIL}" for release in $(echo "$PLAN" | jq --compact-output '.releases[] | select([.artifacts[] | endswith(".rb")] | any)'); do filename=$(echo "$release" | jq '.artifacts[] | select(endswith(".rb"))' --raw-output) name=$(echo "$filename" | sed "s/\.rb$//") version=$(echo "$release" | jq .app_version --raw-output) export PATH="/home/linuxbrew/.linuxbrew/bin:$PATH" brew update # We avoid reformatting user-provided data such as the app description and homepage. brew style --except-cops FormulaAudit/Homepage,FormulaAudit/Desc,FormulaAuditStrict --fix "Formula/${filename}" || true git add "Formula/${filename}" git commit -m "${name} ${version}" done git push origin HEAD announce: needs: - plan - host - publish-homebrew-formula # use "always() && ..." to allow us to wait for all publish jobs while # still allowing individual publish jobs to skip themselves (for prereleases). # "host" however must run to completion, no skipping allowed! if: ${{ always() && needs.host.result == 'success' && (needs.publish-homebrew-formula.result == 'skipped' || needs.publish-homebrew-formula.result == 'success') }} runs-on: "ubuntu-22.04" env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} steps: - uses: actions/checkout@v4 with: persist-credentials: false submodules: recursive
name: Rust on: push: branches: [ "*" ] pull_request: branches: [ "*" ] env: CARGO_TERM_COLOR: always FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true RUST_LOG: info jobs: build: runs-on: ${{ matrix.os }} strategy: matrix: os: [ubuntu-latest, macos-15-intel, macos-latest, windows-latest] features_args: ["", "--no-default-features", "--features nostr"] steps: - uses: actions/checkout@v4 - name: Build ${{ matrix.features_args }} run: cargo build --workspace --verbose ${{ matrix.features_args }} - name: Run workspace tests ${{ matrix.features_args }} run: | cargo test --workspace ${{ matrix.features_args }} -- --test-threads 1 - name: Run get_file_hash_core tests ${{ matrix.features_args }} shell: bash run: | if [[ "${{ matrix.features_args }}" == "--features nostr" ]]; then cargo test -p get_file_hash_core ${{ matrix.features_args }} -- --test-threads 1 --nocapture else cargo test -p get_file_hash_core ${{ matrix.features_args }} -- --test-threads 1 fi - name: Run get_file_hash tests ${{ matrix.features_args }} shell: bash run: | if [[ "${{ matrix.features_args }}" == "--features nostr" ]]; then cargo test -p get_file_hash ${{ matrix.features_args }} -- --test-threads 1 --nocapture else cargo test -p get_file_hash ${{ matrix.features_args }} -- --test-threads 1 fi - name: Build Release ${{ matrix.features_args }} run: cargo build --workspace --release ${{ matrix.features_args }}
# This file was autogenerated by dist: # # Copyright 2022-2024, axodotdev # SPDX-License-Identifier: MIT or Apache-2.0 # # CI that: # # * checks for a Git Tag that looks like a release # * builds artifacts with dist (archives, installers, hashes) # * uploads those artifacts to temporary workflow zip # * on success, uploads the artifacts to a GitHub Release # # Note that the GitHub Release will be created with a generated # title/body based on your changelogs. name: Release permissions: "contents": "write" # This task will run whenever you push a git tag that looks like a version # like "1.0.0", "v0.1.0-prerelease.1", "my-app/0.1.0", "releases/v1.0.0", etc. # Various formats will be parsed into a VERSION and an optional PACKAGE_NAME, where # PACKAGE_NAME must be the name of a Cargo package in your workspace, and VERSION # must be a Cargo-style SemVer Version (must have at least major.minor.patch). # # If PACKAGE_NAME is specified, then the announcement will be for that # package (erroring out if it doesn't have the given version or isn't dist-able). # # If PACKAGE_NAME isn't specified, then the announcement will be for all # (dist-able) packages in the workspace with that version (this mode is # intended for workspaces with only one dist-able package, or with all dist-able # packages versioned/released in lockstep). # # If you push multiple tags at once, separate instances of this workflow will # spin up, creating an independent announcement for each one. However, GitHub # will hard limit this to 3 tags per commit, as it will assume more tags is a # mistake. # # If there's a prerelease-style suffix to the version, then the release(s) # will be marked as a prerelease. on: push: tags: - "**[0-9]+.[0-9]+.[0-9]+*" jobs: # Run 'dist plan' (or host) to determine what tasks we need to do plan: runs-on: "ubuntu-22.04" outputs: val: ${{ steps.plan.outputs.manifest }} tag: ${{ !github.event.pull_request && github.ref_name || '' }} tag-flag: ${{ !github.event.pull_request && format('--tag={0}', github.ref_name) || '' }} publishing: ${{ !github.event.pull_request }} env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} steps: - uses: actions/checkout@v4 with: persist-credentials: false submodules: recursive #- name: Install sccache # run: | # cargo install sccache --version 0.4.0 --locked # echo "$HOME/.cargo/bin" >> $GITHUB_PATH - name: Install dist # we specify bash to get pipefail; it guards against the `curl` command # failing. otherwise `sh` won't catch that `curl` returned non-0 shell: bash run: "curl --proto '=https' --tlsv1.2 -LsSf https://github.com/axodotdev/cargo-dist/releases/download/v0.30.3/cargo-dist-installer.sh | sh" - name: Cache dist uses: actions/upload-artifact@v4 with: name: cargo-dist-cache path: ~/.cargo/bin/dist # sure would be cool if github gave us proper conditionals... # so here's a doubly-nested ternary-via-truthiness to try to provide the best possible # functionality based on whether this is a pull_request, and whether it's from a fork. # (PRs run on the *source* but secrets are usually on the *target* -- that's *good* # but also really annoying to build CI around when it needs secrets to work right.) - id: plan run: | dist ${{ (!github.event.pull_request && format('host --steps=create --tag={0}', github.ref_name)) || 'plan' }} --output-format=json > plan-dist-manifest.json echo "dist ran successfully" cat plan-dist-manifest.json echo "manifest=$(jq -c "." plan-dist-manifest.json)" >> "$GITHUB_OUTPUT" - name: "Upload dist-manifest.json" uses: actions/upload-artifact@v4 with: name: artifacts-plan-dist-manifest path: plan-dist-manifest.json # Build and packages all the platform-specific things build-local-artifacts: name: build-local-artifacts (${{ join(matrix.targets, ', ') }}) # Let the initial task tell us to not run (currently very blunt) needs: - plan if: ${{ fromJson(needs.plan.outputs.val).ci.github.artifacts_matrix.include != null && (needs.plan.outputs.publishing == 'true' || fromJson(needs.plan.outputs.val).ci.github.pr_run_mode == 'upload') }} strategy: fail-fast: false # Target platforms/runners are computed by dist in create-release. # Each member of the matrix has the following arguments: # # - runner: the github runner # - dist-args: cli flags to pass to dist # - install-dist: expression to run to install dist on the runner # # Typically there will be: # - 1 "global" task that builds universal installers # - N "local" tasks that build each platform's binaries and platform-specific installers matrix: ${{ fromJson(needs.plan.outputs.val).ci.github.artifacts_matrix }} runs-on: ${{ matrix.runner }} container: ${{ matrix.container && matrix.container.image || null }} env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} BUILD_MANIFEST_NAME: target/distrib/${{ join(matrix.targets, '-') }}-dist-manifest.json steps: - name: enable windows longpaths run: | git config --global core.longpaths true - uses: actions/checkout@v4 with: persist-credentials: false submodules: recursive #- name: Install sccache # run: | # cargo install sccache --version 0.4.0 --locked # echo "$HOME/.cargo/bin" >> $GITHUB_PATH - name: Install Rust non-interactively if not already installed if: ${{ matrix.container }} run: | if ! command -v cargo > /dev/null 2>&1; then curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y echo "$HOME/.cargo/bin" >> $GITHUB_PATH fi - name: Install dist run: ${{ matrix.install_dist.run }} # Get the dist-manifest - name: Fetch local artifacts uses: actions/download-artifact@v4 with: pattern: artifacts-* path: target/distrib/ merge-multiple: true - name: Install dependencies run: | ${{ matrix.packages_install }} - name: Build artifacts run: | # Actually do builds and make zips and whatnot dist build ${{ needs.plan.outputs.tag-flag }} --print=linkage --output-format=json ${{ matrix.dist_args }} > dist-manifest.json echo "dist ran successfully" - id: cargo-dist name: Post-build # We force bash here just because github makes it really hard to get values up # to "real" actions without writing to env-vars, and writing to env-vars has # inconsistent syntax between shell and powershell. shell: bash run: | # Parse out what we just built and upload it to scratch storage echo "paths<<EOF" >> "$GITHUB_OUTPUT" dist print-upload-files-from-manifest --manifest dist-manifest.json >> "$GITHUB_OUTPUT" echo "EOF" >> "$GITHUB_OUTPUT" cp dist-manifest.json "$BUILD_MANIFEST_NAME" - name: "Upload artifacts" uses: actions/upload-artifact@v4 with: name: artifacts-build-local-${{ join(matrix.targets, '_') }} path: | ${{ steps.cargo-dist.outputs.paths }} ${{ env.BUILD_MANIFEST_NAME }} # Build and package all the platform-agnostic(ish) things build-global-artifacts: needs: - plan - build-local-artifacts runs-on: "ubuntu-22.04" env: BUILD_MANIFEST_NAME: target/distrib/global-dist-manifest.json steps: - uses: actions/checkout@v4 with: persist-credentials: false submodules: recursive #- name: Install sccache # run: | # cargo install sccache --version 0.4.0 --locked # echo "$HOME/.cargo/bin" >> $GITHUB_PATH - name: Install cached dist uses: actions/download-artifact@v4 with: name: cargo-dist-cache path: ~/.cargo/bin/ - run: chmod +x ~/.cargo/bin/dist # Get all the local artifacts for the global tasks to use (for e.g. checksums) - name: Fetch local artifacts uses: actions/download-artifact@v4 with: pattern: artifacts-* path: target/distrib/ merge-multiple: true - id: cargo-dist shell: bash run: | dist build ${{ needs.plan.outputs.tag-flag }} --output-format=json "--artifacts=global" > dist-manifest.json echo "dist ran successfully" # Parse out what we just built and upload it to scratch storage echo "paths<<EOF" >> "$GITHUB_OUTPUT" jq --raw-output ".upload_files[]" dist-manifest.json >> "$GITHUB_OUTPUT" echo "EOF" >> "$GITHUB_OUTPUT" cp dist-manifest.json "$BUILD_MANIFEST_NAME" - name: "Upload artifacts" uses: actions/upload-artifact@v4 with: name: artifacts-build-global path: | ${{ steps.cargo-dist.outputs.paths }} ${{ env.BUILD_MANIFEST_NAME }} # Determines if we should publish/announce host: needs: - plan - build-local-artifacts - build-global-artifacts # Only run if we're "publishing", and only if plan, local and global didn't fail (skipped is fine) if: ${{ always() && needs.plan.result == 'success' && needs.plan.outputs.publishing == 'true' && (needs.build-global-artifacts.result == 'skipped' || needs.build-global-artifacts.result == 'success') && (needs.build-local-artifacts.result == 'skipped' || needs.build-local-artifacts.result == 'success') }} env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} runs-on: "ubuntu-22.04" outputs: val: ${{ steps.host.outputs.manifest }} steps: - uses: actions/checkout@v4 with: persist-credentials: false submodules: recursive #- name: Install sccache # run: | # cargo install sccache --version 0.4.0 --locked # echo "$HOME/.cargo/bin" >> $GITHUB_PATH - name: Install cached dist uses: actions/download-artifact@v4 with: name: cargo-dist-cache path: ~/.cargo/bin/ - run: chmod +x ~/.cargo/bin/dist # Fetch artifacts from scratch-storage - name: Fetch artifacts uses: actions/download-artifact@v4 with: pattern: artifacts-* path: target/distrib/ merge-multiple: true - id: host shell: bash run: | dist host ${{ needs.plan.outputs.tag-flag }} --steps=upload --steps=release --output-format=json > dist-manifest.json echo "artifacts uploaded and released successfully" cat dist-manifest.json echo "manifest=$(jq -c "." dist-manifest.json)" >> "$GITHUB_OUTPUT" - name: "Upload dist-manifest.json" uses: actions/upload-artifact@v4 with: # Overwrite the previous copy name: artifacts-dist-manifest path: dist-manifest.json # Create a GitHub Release while uploading all files to it - name: "Download GitHub Artifacts" uses: actions/download-artifact@v4 with: pattern: artifacts-* path: artifacts merge-multiple: true - name: Cleanup run: | # Remove the granular manifests rm -f artifacts/*-dist-manifest.json - name: Create GitHub Release env: PRERELEASE_FLAG: "${{ fromJson(steps.host.outputs.manifest).announcement_is_prerelease && '--prerelease' || '' }}" ANNOUNCEMENT_TITLE: "${{ fromJson(steps.host.outputs.manifest).announcement_title }}" ANNOUNCEMENT_BODY: "${{ fromJson(steps.host.outputs.manifest).announcement_github_body }}" RELEASE_COMMIT: "${{ github.sha }}" run: | # Write and read notes from a file to avoid quoting breaking things echo "$ANNOUNCEMENT_BODY" > $RUNNER_TEMP/notes.txt gh release create "${{ needs.plan.outputs.tag }}" --target "$RELEASE_COMMIT" $PRERELEASE_FLAG --title "$ANNOUNCEMENT_TITLE" --notes-file "$RUNNER_TEMP/notes.txt" artifacts/* publish-homebrew-formula: needs: - plan - host runs-on: "ubuntu-22.04" env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} PLAN: ${{ needs.plan.outputs.val }} GITHUB_USER: "axo bot" GITHUB_EMAIL: "admin+bot@axo.dev" if: ${{ !fromJson(needs.plan.outputs.val).announcement_is_prerelease || fromJson(needs.plan.outputs.val).publish_prereleases }} steps: - uses: actions/checkout@v4 with: persist-credentials: true repository: "gnostr-org/homebrew-gnostr" token: ${{ secrets.HOMEBREW_TAP_TOKEN }} # So we have access to the formula - name: Fetch homebrew formulae uses: actions/download-artifact@v4 with: pattern: artifacts-* path: Formula/ merge-multiple: true # This is extra complex because you can make your Formula name not match your app name # so we need to find releases with a *.rb file, and publish with that filename. - name: Commit formula files run: | git config --global user.name "${GITHUB_USER}" git config --global user.email "${GITHUB_EMAIL}" for release in $(echo "$PLAN" | jq --compact-output '.releases[] | select([.artifacts[] | endswith(".rb")] | any)'); do filename=$(echo "$release" | jq '.artifacts[] | select(endswith(".rb"))' --raw-output) name=$(echo "$filename" | sed "s/\.rb$//") version=$(echo "$release" | jq .app_version --raw-output) export PATH="/home/linuxbrew/.linuxbrew/bin:$PATH" brew update # We avoid reformatting user-provided data such as the app description and homepage. brew style --except-cops FormulaAudit/Homepage,FormulaAudit/Desc,FormulaAuditStrict --fix "Formula/${filename}" || true git add "Formula/${filename}" git commit -m "${name} ${version}" done git push origin HEAD announce: needs: - plan - host - publish-homebrew-formula # use "always() && ..." to allow us to wait for all publish jobs while # still allowing individual publish jobs to skip themselves (for prereleases). # "host" however must run to completion, no skipping allowed! if: ${{ always() && needs.host.result == 'success' && (needs.publish-homebrew-formula.result == 'skipped' || needs.publish-homebrew-formula.result == 'success') }} runs-on: "ubuntu-22.04" env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} steps: - uses: actions/checkout@v4 with: persist-credentials: false submodules: recursive
# dist plan --output-format=json > plan-dist-manifest.json # Config for 'dist' [workspace] members = ["cargo:.", "cargo:src/get_file_hash_core"] # Config for 'dist' [dist] # The preferred dist version to use in CI (Cargo.toml SemVer syntax) cargo-dist-version = "0.30.3" # CI backends to support ci = "github" # The installers to generate for each app installers = ["shell", "powershell", "homebrew", "msi"] # A GitHub repo to push Homebrew formulas to tap = "gnostr-org/homebrew-gnostr-org" # Path that installers should place binaries in install-path = "CARGO_HOME" # Publish jobs to run in CI publish-jobs = ["homebrew"] # Whether to install an updater program install-updater = true # Target platforms to build apps for (Rust target-triple syntax) targets = ["aarch64-apple-darwin", "aarch64-unknown-linux-gnu", "x86_64-apple-darwin", "x86_64-unknown-linux-gnu", "x86_64-unknown-linux-musl", "x86_64-pc-windows-msvc"] # Skip checking whether the specified configuration files are up to date allow-dirty = ["ci"]
/// deterministic nostr event build example // deterministic nostr event build example use get_file_hash_core::get_file_hash; #[cfg(all(not(debug_assertions), feature = "nostr"))] use get_file_hash_core::{get_git_tracked_files, DEFAULT_GNOSTR_KEY, DEFAULT_PICTURE_URL, DEFAULT_BANNER_URL, publish_nostr_event_if_release, get_repo_announcement_event}; #[cfg(all(not(debug_assertions), feature = "nostr"))] use nostr_sdk::{EventBuilder, Keys, Tag, SecretKey}; #[cfg(all(not(debug_assertions), feature = "nostr"))] use std::fs; use std::path::PathBuf; use sha2::{Digest, Sha256}; #[cfg(all(not(debug_assertions), feature = "nostr"))] use ::hex; #[tokio::main] async fn main() { let manifest_dir = std::env::var("CARGO_MANIFEST_DIR").unwrap(); let is_git_repo = std::path::Path::new(&manifest_dir).join(".git").exists(); #[cfg(all(not(debug_assertions), feature = "nostr"))] #[allow(unused_mut)] let mut git_commit_hash_str = String::new(); #[cfg(all(not(debug_assertions), feature = "nostr"))] #[allow(unused_mut)] let mut git_branch_str = String::new(); println!("cargo:rustc-env=CARGO_PKG_NAME={}", env!("CARGO_PKG_NAME")); println!("cargo:rustc-env=CARGO_PKG_VERSION={}", env!("CARGO_PKG_VERSION")); if is_git_repo { let git_commit_hash_output = std::process::Command::new("git") .args(&["rev-parse", "HEAD"]) .stdout(std::process::Stdio::piped()) .stderr(std::process::Stdio::piped()) .output() .expect("Failed to execute git command for commit hash"); let git_commit_hash_str = if git_commit_hash_output.status.success() && !git_commit_hash_output.stdout.is_empty() { String::from_utf8(git_commit_hash_output.stdout).unwrap().trim().to_string() } else { println!("cargo:warning=Git commit hash command failed or returned empty. Status: {:?}, Stderr: {}", git_commit_hash_output.status, String::from_utf8_lossy(&git_commit_hash_output.stderr)); String::new() }; println!("cargo:rustc-env=GIT_COMMIT_HASH={}", git_commit_hash_str); let git_branch_output = std::process::Command::new("git") .args(&["rev-parse", "--abbrev-ref", "HEAD"]) .stdout(std::process::Stdio::piped()) .stderr(std::process::Stdio::piped()) .output() .expect("Failed to execute git command for branch name"); let git_branch_str = if git_branch_output.status.success() && !git_branch_output.stdout.is_empty() { String::from_utf8(git_branch_output.stdout).unwrap().trim().to_string() } else { println!("cargo:warning=Git branch command failed or returned empty. Status: {:?}, Stderr: {}", git_branch_output.status, String::from_utf8_lossy(&git_branch_output.stderr)); String::new() }; println!("cargo:rustc-env=GIT_BRANCH={}", git_branch_str); } else { println!("cargo:rustc-env=GIT_COMMIT_HASH="); println!("cargo:rustc-env=GIT_BRANCH="); } println!("cargo:rerun-if-changed=.git/HEAD"); //#[cfg(all(not(debug_assertions), feature = "nostr"))] //let relay_urls = get_file_hash_core::get_relay_urls(); let cargo_toml_hash = get_file_hash!("Cargo.toml"); println!("cargo:rustc-env=CARGO_TOML_HASH={}", cargo_toml_hash); let lib_hash = get_file_hash!("src/lib.rs"); println!("cargo:rustc-env=LIB_HASH={}", lib_hash); let build_hash = get_file_hash!("build.rs"); println!("cargo:rustc-env=BUILD_HASH={}", build_hash); println!("cargo:rerun-if-changed=Cargo.toml"); println!("cargo:rerun-if-changed=src/lib.rs"); println!("cargo:rerun-if-changed=build.rs"); let online_relays_csv_path = PathBuf::from(&manifest_dir).join("src/get_file_hash_core/src/online_relays_gps.csv"); if online_relays_csv_path.exists() { println!("cargo:rerun-if-changed={}", online_relays_csv_path.to_str().unwrap()); } #[cfg(all(not(debug_assertions), feature = "nostr"))] if cfg!(not(debug_assertions)) { println!("cargo:warning=Nostr feature enabled: Build may take longer due to network operations (publishing events to relays)."); // This code only runs in release builds let package_version = std::env::var("CARGO_PKG_VERSION").unwrap(); let output_dir = PathBuf::from(format!(".gnostr/build/{}", package_version)); if let Err(e) = fs::create_dir_all(&output_dir) { println!("cargo:warning=Failed to create output directory {}: {}", output_dir.display(), e); } let files_to_publish: Vec<String> = get_git_tracked_files(&PathBuf::from(&manifest_dir)); // Initialize client and keys once let initial_keys = Keys::new(SecretKey::from_hex(&hex::encode(Sha256::digest("initial_seed".as_bytes()))).expect("Failed to create initial Nostr keys")); let mut client = nostr_sdk::Client::new(initial_keys.clone()); let mut relay_urls = get_file_hash_core::get_relay_urls(); // Add relays to the client for relay_url in relay_urls.iter() { if let Err(e) = client.add_relay(relay_url).await { println!("cargo:warning=Failed to add relay {}: {}", relay_url, e); } } client.connect().await; println!("cargo:warning=Added and connected to {} relays.", relay_urls.len()); let mut published_event_ids: Vec<Tag> = Vec::new(); let mut total_bytes_sent: usize = 0; for file_path_str in &files_to_publish { println!("cargo:warning=Processing file: {}", file_path_str); match fs::read(file_path_str) { Ok(bytes) => { let mut hasher = Sha256::new(); hasher.update(&bytes); let result = hasher.finalize(); let file_hash_hex = hex::encode(result); match SecretKey::from_hex(&file_hash_hex.clone()) { Ok(secret_key) => { let keys = Keys::new(secret_key); let content = String::from_utf8_lossy(&bytes).into_owned(); let tags = vec![ Tag::parse(["file", file_path_str].iter().map(ToString::to_string).collect::<Vec<String>>()).unwrap(), Tag::parse(["version", &package_version].iter().map(ToString::to_string).collect::<Vec<String>>()).unwrap(), ]; let event_builder = EventBuilder::text_note(content).tags(tags); if let Some(event_id) = publish_nostr_event_if_release(&mut client, file_hash_hex, keys.clone(), event_builder, &mut relay_urls, file_path_str, &output_dir, &mut total_bytes_sent).await { published_event_ids.push(Tag::event(event_id)); } // Publish metadata event get_file_hash_core::publish_metadata_event( &keys, &relay_urls, DEFAULT_PICTURE_URL, DEFAULT_BANNER_URL, file_path_str, ).await; } Err(e) => { println!("cargo:warning=Failed to derive Nostr secret key for {}: {}", file_path_str, e); } } } Err(e) => { println!("cargo:warning=Failed to read file {}: {}", file_path_str, e); } } } // Create and publish the build_manifest if !published_event_ids.is_empty() { //TODO this will be either the default or detected from env vars PRIVATE_KEY let keys = Keys::new(SecretKey::from_hex(DEFAULT_GNOSTR_KEY).expect("Failed to create Nostr keys from DEFAULT_GNOSTR_KEY")); let cloned_keys = keys.clone(); let content = format!("Build manifest for get_file_hash v{}", package_version); let mut tags = vec![ Tag::parse(["build_manifest", &package_version].iter().map(ToString::to_string).collect::<Vec<String>>()).unwrap(), Tag::parse(["build_manifest", &package_version].iter().map(ToString::to_string).collect::<Vec<String>>()).unwrap(), Tag::parse(["build_manifest", &package_version].iter().map(ToString::to_string).collect::<Vec<String>>()).unwrap(), Tag::parse(["build_manifest", &package_version].iter().map(ToString::to_string).collect::<Vec<String>>()).unwrap(), ]; tags.extend(published_event_ids); let event_builder = EventBuilder::text_note(content.clone()).tags(tags); if let Some(event_id) = publish_nostr_event_if_release( &mut client, hex::encode(Sha256::digest(content.as_bytes())), keys, event_builder, &mut relay_urls, "build_manifest.json", &output_dir, &mut total_bytes_sent, ).await { let build_manifest_event_id = Some(event_id); // Publish metadata event for the build manifest get_file_hash_core::publish_metadata_event( &cloned_keys, // Use reference to cloned keys here &relay_urls, DEFAULT_PICTURE_URL, DEFAULT_BANNER_URL, &format!("build_manifest:{}", package_version), ).await; let git_commit_hash = &git_commit_hash_str; let git_branch = &git_branch_str; let repo_url = std::env::var("CARGO_PKG_REPOSITORY").unwrap(); let repo_name = std::env::var("CARGO_PKG_NAME").unwrap(); let repo_description = std::env::var("CARGO_PKG_DESCRIPTION").unwrap(); let output_dir = PathBuf::from(format!(".gnostr/build/{}", package_version)); if let Err(e) = fs::create_dir_all(&output_dir) { println!("cargo:warning=Failed to create output directory {}: {}", output_dir.display(), e); } let announcement_keys = Keys::new(SecretKey::from_hex(build_manifest_event_id.unwrap().to_hex().as_str()).expect("Failed to create Nostr keys from build_manifest_event_id")); let announcement_pubkey_hex = announcement_keys.public_key().to_string(); // Publish NIP-34 Repository Announcement if let Some(_event_id) = get_repo_announcement_event( &mut client, &announcement_keys, &relay_urls, &repo_url, &repo_name, &repo_description, &git_commit_hash, &git_branch, &output_dir, &announcement_pubkey_hex ).await { // Successfully published announcement } } } println!("cargo:warning=Total bytes sent to Nostr relays: {} bytes ({} MB)", total_bytes_sent, total_bytes_sent as f64 / 1024.0 / 1024.0); } } // deterministic nostr event build example
# `get_file_hash` macro This project provides a Rust procedural macro, `get_file_hash!`, designed to compute the SHA-256 hash of a specified file at compile time. This hash is then embedded directly into your compiled executable. This feature is invaluable for: * **Integrity Verification:** Ensuring the deployed code hasn't been tampered with. * **Versioning:** Embedding a unique identifier linked to the exact source code version. * **Cache Busting:** Generating unique names for assets based on their content. ## Project Structure * `get_file_hash_core`: A foundational crate containing the `get_file_hash!` macro definition. * `get_file_hash`: The main library crate that re-exports the macro. * `src/bin/get_file_hash.rs`: An example executable demonstrating the macro's usage by hashing its own source file and updating this `README.md`. * `build.rs`: A build script that also utilizes the `get_file_hash!` macro to hash `Cargo.toml` during the build process. ## Usage of `get_file_hash!` Macro To use the `get_file_hash!` macro, ensure you have `get_file_hash` (or `get_file_hash_core` for direct usage) as a dependency in your `Cargo.toml`. ### Example ```rust use get_file_hash::get_file_hash; use get_file_hash::CARGO_TOML_HASH; use sha2::{Digest, Sha256}; fn main() { // The macro resolves the path relative to CARGO_MANIFEST_DIR let readme_hash = get_file_hash!("src/bin/readme.rs"); let lib_hash = get_file_hash!("src/lib.rs"); println!("The SHA-256 hash of src/lib.rs is: {}", lib_hash); println!("The SHA-256 hash of src/bin/readme.rs is: {}", readme_hash); println!("The SHA-256 hash of Cargo.toml is: {}", CARGO_TOML_HASH); } ``` ## Release ## [`README.md`](./README.md) ```bash cargo run --bin readme > README.md ``` ## [`src/bin/readme.rs`](src/bin/readme.rs) * **Target File:** `src/bin/readme.rs` ## NIP-34 Integration: Git Repository Events on Nostr This library provides a set of powerful macros and functions for integrating Git repository events with the Nostr protocol, adhering to the [NIP-34: Git Repositories on Nostr](https://github.com/nostr-protocol/nips/blob/master/34.md) specification. These tools allow you to publish various Git-related events to Nostr relays, enabling decentralized tracking and collaboration for your code repositories. ### Available NIP-34 Macros Each macro provides a convenient way to publish specific NIP-34 event kinds: * [`repository_announcement!`](#repository_announcement) * Publishes a `Repository Announcement` event (Kind 30617) to announce a new or updated Git repository. * [`publish_patch!`](#publish_patch) * Publishes a `Patch` event (Kind 1617) containing a Git patch (diff) for a specific commit. * [`publish_pull_request!`](#publish_pull_request) * Publishes a `Pull Request` event (Kind 1618) to propose changes and facilitate code review. * [`publish_pr_update!`](#publish_pr_update) * Publishes a `Pull Request Update` event (Kind 1619) to update an existing pull request. * [`publish_repository_state!`](#publish_repository_state) * Publishes a `Repository State` event (Kind 1620) to announce the current state of a branch (e.g., its latest commit). * [`publish_issue!`](#publish_issue) * Publishes an `Issue` event (Kind 1621) to report bugs, request features, or track tasks. ### Running NIP-34 Examples To see these macros in action, navigate to the `examples/` directory and run each example individually with the `nostr` feature enabled: ```bash cargo run --example repository_announcement --features nostr cargo run --example publish_patch --features nostr cargo run --example publish_pull_request --features nostr cargo run --example publish_pr_update --features nostr cargo run --example publish_repository_state --features nostr cargo run --example publish_issue --features nostr ``` * **SHA-256 Hash:** 6c6325c5a4c14f44cbda6ca53179ab3d6666ce7c916365668c6dd1d79215db59 * **Status:** Integrity Verified.. ## ## [`build.rs`](build.rs) * **Target File:** `build.rs` * **SHA-256 Hash:** 20c958c8cbb5c77cf5eb3763b6da149b61241d328df52d39b7aa97903305c889 * **Status:** Integrity Verified.. ## ## [`Cargo.toml`](Cargo.toml) * **Target File:** `Cargo.toml` * **SHA-256 Hash:** e3f392bf23b5fb40902acd313a8c76d1943060b6805ea8615de62f9baf0c6513 * **Status:** Integrity Verified.. ## ## [`src/lib.rs`](src/lib.rs) * **Target File:** `src/lib.rs` * **SHA-256 Hash:** 591593482a6c9aac8793aa1e488e613f52a4effb1ec3465fd9d6a54537f2b123 * **Status:** Integrity Verified..
[workspace] members = [".", "src/get_file_hash_core"] [workspace.package] version = "0.3.3" edition = "2024" license = "MIT" authors = ["gnostr admin@gnostr.org"] documentation = "https://github.com/gnostr-org/get_file_hash#readme" homepage = "https://github.com/gnostr-org/get_file_hash" repository = "https://github.com/gnostr-org/get_file_hash" description = "A utility crate providing a procedural macro to compute and embed file hashes at compile time." [package] name = "get_file_hash" version.workspace = true edition.workspace = true description.workspace = true repository.workspace = true homepage.workspace = true authors.workspace = true license.workspace = true [package.metadata.wix] upgrade-guid = "DED69220-26E3-4406-B564-7F2B58C56F57" path-guid = "8DB39A25-8B99-4C25-8CF5-4704353C7C6E" license = false eula = false [features] nostr = ["dep:nostr", "dep:nostr-sdk", "dep:hex"] [workspace.dependencies] get_file_hash_core = { features = ["nostr"], path = "src/get_file_hash_core", version = "0.3.3" } sha2 = "0.11.0" nostr = "0.44.2" nostr-sdk = "0.44.0" hex = "0.4.2" tokio = "1" serde_json = "1.0" csv = { version = "1.3.0", default-features = false } url = "2.5.0" reqwest = { version = "0.12.0", default-features = false } tempfile = "3.27.0" rand = "0.8" frost-secp256k1-tr = "3.0.0-rc.0" serial_test = { version = "3.4.0", features = ["test_logging"] } log = "0.4" [dependencies] get_file_hash_core = { workspace = true, features = ["nostr"] } sha2 = { workspace = true } nostr = { workspace = true, optional = true } nostr-sdk = { workspace = true, optional = true } hex = { workspace = true, optional = true } tokio = { workspace = true, features = ["full"] } frost-secp256k1-tr = { workspace = true } rand = { workspace = true } serde_json.workspace = true [build-dependencies] get_file_hash_core = { workspace = true, features = ["nostr"] } sha2 = { workspace = true } serde_json = { workspace = true } tokio = { workspace = true, features = ["full"] } nostr = { workspace = true } nostr-sdk = { workspace = true } hex = { workspace = true } # The profile that 'dist' will build with [profile.dist] inherits = "release" lto = "thin" [dev-dependencies] serial_test = { workspace = true }
# `build.rs` Documentation This document explains the functionality of the `build.rs` script in this project. The `build.rs` script is a special Rust file that, if present, Cargo will compile and run *before* compiling the rest of your package. It's typically used for tasks that need to be performed during the build process, such as generating code, setting environment variables, or performing conditional compilation. ## Core Functionality The `build.rs` script in this project performs the following key functions: 1. **Environment Variable Injection:** It computes various project-related values at compile time and injects them as environment variables (`CARGO_RUSTC_ENV=...`) that can be accessed by the main crate using `env!("VAR_NAME")`. This includes: * `CARGO_PKG_NAME`: The name of the current package (from `Cargo.toml`). * `CARGO_PKG_VERSION`: The version of the current package (from `Cargo.toml`). * `GIT_COMMIT_HASH`: The full commit hash of the current Git HEAD (if in a Git repository). * `GIT_BRANCH`: The name of the current Git branch (if in a Git repository). * `CARGO_TOML_HASH`: The SHA-256 hash of the `Cargo.toml` file. * `LIB_HASH`: The SHA-256 hash of the `src/lib.rs` file. * `BUILD_HASH`: The SHA-256 hash of the `build.rs` file itself. 2. **Rerun Conditions:** It tells Cargo when to re-run the build script. This ensures that the injected environment variables and any conditional compilation logic are up-to-date if relevant files change: * `Cargo.toml` * `src/lib.rs` * `build.rs` * `.git/HEAD` (to detect changes in the Git repository like new commits or branch switches). * `src/get_file_hash_core/src/online_relays_gps.csv` (conditionally, if the file exists). 3. **Conditional Nostr Event Publishing (Release Builds with `nostr` feature):** If the project is being compiled in **release mode (`--release`)** and the **`nostr` feature is enabled (`--features nostr`)**, the `build.rs` script will connect to Nostr relays and publish events. This is intended for "deterministic Nostr event build examples" as indicated by the comments in the file. * **Relay Management:** It retrieves a list of default relay URLs. During event publishing, it identifies and removes "unfriendly" or unresponsive relays (e.g., those with timeout, connection issues, or spam blocks) from the list for subsequent publications. * **File Hashing and Key Generation:** For each Git-tracked file (when in a Git repository), it computes its SHA-256 hash. This hash is then used to derive a Nostr `SecretKey`. * **Event Creation:** * **Individual File Events:** For each Git-tracked file, a Nostr `text_note` event is created. This event includes tags for: * `#file`: The path of the file. * `#version`: The package version. * `#commit`: The Git commit hash (if in a Git repository). * `#branch`: The Git branch name (if in a Git repository). * **Metadata Event:** It publishes a metadata event using `get_file_hash_core::publish_metadata_event`. * **Linking Event (Build Manifest):** After processing all individual files, if any events were published, a final "build manifest" `text_note` event is created. This event links to all the individual file events that were published during the build using event tags. * **Output Storage:** The JSON representation of successfully published Nostr events (specifically the `EventId`) is saved to `~/.gnostr/build/{package_version}/{file_path_str_sanitized}/{hash}/{public_key}/{event_id}.json`. This provides a local record of what was published. ### `publish_nostr_event_if_release` Function This asynchronous helper function is responsible for: * Adding relays to the Nostr client. * Connecting to relays. * Signing the provided `EventBuilder` to create an `Event`. * Sending the event to the configured relays. * Logging success or failure for each relay. * Identifying and removing unresponsive relays from the `relay_urls` list. * Saving the published event's JSON to the local filesystem. ### `should_remove_relay` Function This helper function determines if a relay should be considered "unfriendly" or unresponsive based on common error messages received during Nostr event publication. ## Usage To prevent 'Too many open files' errors, especially during builds and tests involving numerous file operations or subprocesses (like `git ls-files` or parallel test execution), it may be necessary to increase the file descriptor limit. * **For local development**: Run `ulimit -n 4096` in your terminal session before executing `cargo build` or `cargo test`. This setting is session-specific. * **For CI environments**: The `.github/workflows/rust.yml` workflow is configured to set `ulimit -n 4096` for relevant test steps to ensure consistent execution. The values set by `build.rs` can be accessed in your Rust code (e.g., `src/lib.rs`) at compile time using the `env!` macro. For example: ```rust pub const CARGO_PKG_VERSION: &str = env!("CARGO_PKG_VERSION"); ``` The Nostr event publishing functionality of `build.rs` is primarily for release builds with the `nostr` feature enabled, allowing for the automatic, deterministic publication of project state to the Nostr network as part of the CI/CD pipeline. ## Example Commands To interact with the `build.rs` script's features, especially those related to Nostr event publishing, you can use the following `cargo` commands: * **Build in release mode with Nostr feature (verbose output):** ```bash cargo build --release --workspace --features nostr -vv ``` * **Run tests for `get_file_hash_core` sequentially with Nostr feature and verbose logging (as in CI):** ```bash RUST_LOG=info,nostr_sdk=debug,frost=debug cargo test -p get_file_hash_core --features nostr -- --test-threads 1 --nocapture ``` * **Run all workspace tests in release mode with Nostr feature:** ```bash cargo test --workspace --release --features nostr ``` * **Build `get_file_hash_core` in release mode with Nostr feature (very verbose output):** ```bash cargo build --release --features nostr -vv -p get_file_hash_core ``` * **Run `get_file_hash_core` tests in release mode with Nostr feature (very verbose output):** ```bash cargo test --release --features nostr -vv -p get_file_hash_core ```
name: Rust on: push: branches: [ "*" ] pull_request: branches: [ "*" ] env: CARGO_TERM_COLOR: always FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true RUST_LOG: info jobs: build: runs-on: ${{ matrix.os }} strategy: matrix: os: [ubuntu-latest, macos-15-intel, macos-latest, windows-latest] features_args: ["", "--no-default-features", "--features nostr"] steps: - uses: actions/checkout@v4 - name: Build ${{ matrix.features_args }} run: cargo build --workspace --verbose ${{ matrix.features_args }} - name: Run workspace tests ${{ matrix.features_args }} run: | cargo test --workspace ${{ matrix.features_args }} -- --test-threads 1 - name: Run get_file_hash_core tests ${{ matrix.features_args }} shell: bash run: | if [[ "${{ matrix.features_args }}" == "--features nostr" ]]; then cargo test -p get_file_hash_core ${{ matrix.features_args }} -- --test-threads 1 --nocapture else cargo test -p get_file_hash_core ${{ matrix.features_args }} -- --test-threads 1 fi - name: Run get_file_hash tests ${{ matrix.features_args }} shell: bash run: | if [[ "${{ matrix.features_args }}" == "--features nostr" ]]; then cargo test -p get_file_hash ${{ matrix.features_args }} -- --test-threads 1 --nocapture else cargo test -p get_file_hash ${{ matrix.features_args }} -- --test-threads 1 fi - name: Build Release ${{ matrix.features_args }} run: cargo build --workspace --release ${{ matrix.features_args }}
# This file was autogenerated by dist: # # Copyright 2022-2024, axodotdev # SPDX-License-Identifier: MIT or Apache-2.0 # # CI that: # # * checks for a Git Tag that looks like a release # * builds artifacts with dist (archives, installers, hashes) # * uploads those artifacts to temporary workflow zip # * on success, uploads the artifacts to a GitHub Release # # Note that the GitHub Release will be created with a generated # title/body based on your changelogs. name: Release permissions: "contents": "write" # This task will run whenever you push a git tag that looks like a version # like "1.0.0", "v0.1.0-prerelease.1", "my-app/0.1.0", "releases/v1.0.0", etc. # Various formats will be parsed into a VERSION and an optional PACKAGE_NAME, where # PACKAGE_NAME must be the name of a Cargo package in your workspace, and VERSION # must be a Cargo-style SemVer Version (must have at least major.minor.patch). # # If PACKAGE_NAME is specified, then the announcement will be for that # package (erroring out if it doesn't have the given version or isn't dist-able). # # If PACKAGE_NAME isn't specified, then the announcement will be for all # (dist-able) packages in the workspace with that version (this mode is # intended for workspaces with only one dist-able package, or with all dist-able # packages versioned/released in lockstep). # # If you push multiple tags at once, separate instances of this workflow will # spin up, creating an independent announcement for each one. However, GitHub # will hard limit this to 3 tags per commit, as it will assume more tags is a # mistake. # # If there's a prerelease-style suffix to the version, then the release(s) # will be marked as a prerelease. on: push: tags: - "**[0-9]+.[0-9]+.[0-9]+*" jobs: # Run 'dist plan' (or host) to determine what tasks we need to do plan: runs-on: "ubuntu-22.04" outputs: val: ${{ steps.plan.outputs.manifest }} tag: ${{ !github.event.pull_request && github.ref_name || '' }} tag-flag: ${{ !github.event.pull_request && format('--tag={0}', github.ref_name) || '' }} publishing: ${{ !github.event.pull_request }} env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} steps: - uses: actions/checkout@v4 with: persist-credentials: false submodules: recursive #- name: Install sccache # run: | # cargo install sccache --version 0.4.0 --locked # echo "$HOME/.cargo/bin" >> $GITHUB_PATH - name: Install dist # we specify bash to get pipefail; it guards against the `curl` command # failing. otherwise `sh` won't catch that `curl` returned non-0 shell: bash run: "curl --proto '=https' --tlsv1.2 -LsSf https://github.com/axodotdev/cargo-dist/releases/download/v0.30.3/cargo-dist-installer.sh | sh" - name: Cache dist uses: actions/upload-artifact@v4 with: name: cargo-dist-cache path: ~/.cargo/bin/dist # sure would be cool if github gave us proper conditionals... # so here's a doubly-nested ternary-via-truthiness to try to provide the best possible # functionality based on whether this is a pull_request, and whether it's from a fork. # (PRs run on the *source* but secrets are usually on the *target* -- that's *good* # but also really annoying to build CI around when it needs secrets to work right.) - id: plan run: | dist ${{ (!github.event.pull_request && format('host --steps=create --tag={0}', github.ref_name)) || 'plan' }} --output-format=json > plan-dist-manifest.json echo "dist ran successfully" cat plan-dist-manifest.json echo "manifest=$(jq -c "." plan-dist-manifest.json)" >> "$GITHUB_OUTPUT" - name: "Upload dist-manifest.json" uses: actions/upload-artifact@v4 with: name: artifacts-plan-dist-manifest path: plan-dist-manifest.json # Build and packages all the platform-specific things build-local-artifacts: name: build-local-artifacts (${{ join(matrix.targets, ', ') }}) # Let the initial task tell us to not run (currently very blunt) needs: - plan if: ${{ fromJson(needs.plan.outputs.val).ci.github.artifacts_matrix.include != null && (needs.plan.outputs.publishing == 'true' || fromJson(needs.plan.outputs.val).ci.github.pr_run_mode == 'upload') }} strategy: fail-fast: false # Target platforms/runners are computed by dist in create-release. # Each member of the matrix has the following arguments: # # - runner: the github runner # - dist-args: cli flags to pass to dist # - install-dist: expression to run to install dist on the runner # # Typically there will be: # - 1 "global" task that builds universal installers # - N "local" tasks that build each platform's binaries and platform-specific installers matrix: ${{ fromJson(needs.plan.outputs.val).ci.github.artifacts_matrix }} runs-on: ${{ matrix.runner }} container: ${{ matrix.container && matrix.container.image || null }} env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} BUILD_MANIFEST_NAME: target/distrib/${{ join(matrix.targets, '-') }}-dist-manifest.json steps: - name: enable windows longpaths run: | git config --global core.longpaths true - uses: actions/checkout@v4 with: persist-credentials: false submodules: recursive #- name: Install sccache # run: | # cargo install sccache --version 0.4.0 --locked # echo "$HOME/.cargo/bin" >> $GITHUB_PATH - name: Install Rust non-interactively if not already installed if: ${{ matrix.container }} run: | if ! command -v cargo > /dev/null 2>&1; then curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y echo "$HOME/.cargo/bin" >> $GITHUB_PATH fi - name: Install dist run: ${{ matrix.install_dist.run }} # Get the dist-manifest - name: Fetch local artifacts uses: actions/download-artifact@v4 with: pattern: artifacts-* path: target/distrib/ merge-multiple: true - name: Install dependencies run: | ${{ matrix.packages_install }} - name: Build artifacts run: | # Actually do builds and make zips and whatnot dist build ${{ needs.plan.outputs.tag-flag }} --print=linkage --output-format=json ${{ matrix.dist_args }} > dist-manifest.json echo "dist ran successfully" - id: cargo-dist name: Post-build # We force bash here just because github makes it really hard to get values up # to "real" actions without writing to env-vars, and writing to env-vars has # inconsistent syntax between shell and powershell. shell: bash run: | # Parse out what we just built and upload it to scratch storage echo "paths<<EOF" >> "$GITHUB_OUTPUT" dist print-upload-files-from-manifest --manifest dist-manifest.json >> "$GITHUB_OUTPUT" echo "EOF" >> "$GITHUB_OUTPUT" cp dist-manifest.json "$BUILD_MANIFEST_NAME" - name: "Upload artifacts" uses: actions/upload-artifact@v4 with: name: artifacts-build-local-${{ join(matrix.targets, '_') }} path: | ${{ steps.cargo-dist.outputs.paths }} ${{ env.BUILD_MANIFEST_NAME }} # Build and package all the platform-agnostic(ish) things build-global-artifacts: needs: - plan - build-local-artifacts runs-on: "ubuntu-22.04" env: BUILD_MANIFEST_NAME: target/distrib/global-dist-manifest.json steps: - uses: actions/checkout@v4 with: persist-credentials: false submodules: recursive #- name: Install sccache # run: | # cargo install sccache --version 0.4.0 --locked # echo "$HOME/.cargo/bin" >> $GITHUB_PATH - name: Install cached dist uses: actions/download-artifact@v4 with: name: cargo-dist-cache path: ~/.cargo/bin/ - run: chmod +x ~/.cargo/bin/dist # Get all the local artifacts for the global tasks to use (for e.g. checksums) - name: Fetch local artifacts uses: actions/download-artifact@v4 with: pattern: artifacts-* path: target/distrib/ merge-multiple: true - id: cargo-dist shell: bash run: | dist build ${{ needs.plan.outputs.tag-flag }} --output-format=json "--artifacts=global" > dist-manifest.json echo "dist ran successfully" # Parse out what we just built and upload it to scratch storage echo "paths<<EOF" >> "$GITHUB_OUTPUT" jq --raw-output ".upload_files[]" dist-manifest.json >> "$GITHUB_OUTPUT" echo "EOF" >> "$GITHUB_OUTPUT" cp dist-manifest.json "$BUILD_MANIFEST_NAME" - name: "Upload artifacts" uses: actions/upload-artifact@v4 with: name: artifacts-build-global path: | ${{ steps.cargo-dist.outputs.paths }} ${{ env.BUILD_MANIFEST_NAME }} # Determines if we should publish/announce host: needs: - plan - build-local-artifacts - build-global-artifacts # Only run if we're "publishing", and only if plan, local and global didn't fail (skipped is fine) if: ${{ always() && needs.plan.result == 'success' && needs.plan.outputs.publishing == 'true' && (needs.build-global-artifacts.result == 'skipped' || needs.build-global-artifacts.result == 'success') && (needs.build-local-artifacts.result == 'skipped' || needs.build-local-artifacts.result == 'success') }} env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} runs-on: "ubuntu-22.04" outputs: val: ${{ steps.host.outputs.manifest }} steps: - uses: actions/checkout@v4 with: persist-credentials: false submodules: recursive #- name: Install sccache # run: | # cargo install sccache --version 0.4.0 --locked # echo "$HOME/.cargo/bin" >> $GITHUB_PATH - name: Install cached dist uses: actions/download-artifact@v4 with: name: cargo-dist-cache path: ~/.cargo/bin/ - run: chmod +x ~/.cargo/bin/dist # Fetch artifacts from scratch-storage - name: Fetch artifacts uses: actions/download-artifact@v4 with: pattern: artifacts-* path: target/distrib/ merge-multiple: true - id: host shell: bash run: | dist host ${{ needs.plan.outputs.tag-flag }} --steps=upload --steps=release --output-format=json > dist-manifest.json echo "artifacts uploaded and released successfully" cat dist-manifest.json echo "manifest=$(jq -c "." dist-manifest.json)" >> "$GITHUB_OUTPUT" - name: "Upload dist-manifest.json" uses: actions/upload-artifact@v4 with: # Overwrite the previous copy name: artifacts-dist-manifest path: dist-manifest.json # Create a GitHub Release while uploading all files to it - name: "Download GitHub Artifacts" uses: actions/download-artifact@v4 with: pattern: artifacts-* path: artifacts merge-multiple: true - name: Cleanup run: | # Remove the granular manifests rm -f artifacts/*-dist-manifest.json - name: Create GitHub Release env: PRERELEASE_FLAG: "${{ fromJson(steps.host.outputs.manifest).announcement_is_prerelease && '--prerelease' || '' }}" ANNOUNCEMENT_TITLE: "${{ fromJson(steps.host.outputs.manifest).announcement_title }}" ANNOUNCEMENT_BODY: "${{ fromJson(steps.host.outputs.manifest).announcement_github_body }}" RELEASE_COMMIT: "${{ github.sha }}" run: | # Write and read notes from a file to avoid quoting breaking things echo "$ANNOUNCEMENT_BODY" > $RUNNER_TEMP/notes.txt gh release create "${{ needs.plan.outputs.tag }}" --target "$RELEASE_COMMIT" $PRERELEASE_FLAG --title "$ANNOUNCEMENT_TITLE" --notes-file "$RUNNER_TEMP/notes.txt" artifacts/* publish-homebrew-formula: needs: - plan - host runs-on: "ubuntu-22.04" env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} PLAN: ${{ needs.plan.outputs.val }} GITHUB_USER: "axo bot" GITHUB_EMAIL: "admin+bot@axo.dev" if: ${{ !fromJson(needs.plan.outputs.val).announcement_is_prerelease || fromJson(needs.plan.outputs.val).publish_prereleases }} steps: - uses: actions/checkout@v4 with: persist-credentials: true repository: "gnostr-org/homebrew-gnostr" token: ${{ secrets.HOMEBREW_TAP_TOKEN }} # So we have access to the formula - name: Fetch homebrew formulae uses: actions/download-artifact@v4 with: pattern: artifacts-* path: Formula/ merge-multiple: true # This is extra complex because you can make your Formula name not match your app name # so we need to find releases with a *.rb file, and publish with that filename. - name: Commit formula files run: | git config --global user.name "${GITHUB_USER}" git config --global user.email "${GITHUB_EMAIL}" for release in $(echo "$PLAN" | jq --compact-output '.releases[] | select([.artifacts[] | endswith(".rb")] | any)'); do filename=$(echo "$release" | jq '.artifacts[] | select(endswith(".rb"))' --raw-output) name=$(echo "$filename" | sed "s/\.rb$//") version=$(echo "$release" | jq .app_version --raw-output) export PATH="/home/linuxbrew/.linuxbrew/bin:$PATH" brew update # We avoid reformatting user-provided data such as the app description and homepage. brew style --except-cops FormulaAudit/Homepage,FormulaAudit/Desc,FormulaAuditStrict --fix "Formula/${filename}" || true git add "Formula/${filename}" git commit -m "${name} ${version}" done git push origin HEAD announce: needs: - plan - host - publish-homebrew-formula # use "always() && ..." to allow us to wait for all publish jobs while # still allowing individual publish jobs to skip themselves (for prereleases). # "host" however must run to completion, no skipping allowed! if: ${{ always() && needs.host.result == 'success' && (needs.publish-homebrew-formula.result == 'skipped' || needs.publish-homebrew-formula.result == 'success') }} runs-on: "ubuntu-22.04" env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} steps: - uses: actions/checkout@v4 with: persist-credentials: false submodules: recursive
# This file was autogenerated by dist: # # Copyright 2022-2024, axodotdev # SPDX-License-Identifier: MIT or Apache-2.0 # # CI that: # # * checks for a Git Tag that looks like a release # * builds artifacts with dist (archives, installers, hashes) # * uploads those artifacts to temporary workflow zip # * on success, uploads the artifacts to a GitHub Release # # Note that the GitHub Release will be created with a generated # title/body based on your changelogs. name: Release permissions: "contents": "write" # This task will run whenever you push a git tag that looks like a version # like "1.0.0", "v0.1.0-prerelease.1", "my-app/0.1.0", "releases/v1.0.0", etc. # Various formats will be parsed into a VERSION and an optional PACKAGE_NAME, where # PACKAGE_NAME must be the name of a Cargo package in your workspace, and VERSION # must be a Cargo-style SemVer Version (must have at least major.minor.patch). # # If PACKAGE_NAME is specified, then the announcement will be for that # package (erroring out if it doesn't have the given version or isn't dist-able). # # If PACKAGE_NAME isn't specified, then the announcement will be for all # (dist-able) packages in the workspace with that version (this mode is # intended for workspaces with only one dist-able package, or with all dist-able # packages versioned/released in lockstep). # # If you push multiple tags at once, separate instances of this workflow will # spin up, creating an independent announcement for each one. However, GitHub # will hard limit this to 3 tags per commit, as it will assume more tags is a # mistake. # # If there's a prerelease-style suffix to the version, then the release(s) # will be marked as a prerelease. on: push: tags: - "**[0-9]+.[0-9]+.[0-9]+*" jobs: # Run 'dist plan' (or host) to determine what tasks we need to do plan: runs-on: "ubuntu-22.04" outputs: val: ${{ steps.plan.outputs.manifest }} tag: ${{ !github.event.pull_request && github.ref_name || '' }} tag-flag: ${{ !github.event.pull_request && format('--tag={0}', github.ref_name) || '' }} publishing: ${{ !github.event.pull_request }} env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} steps: - uses: actions/checkout@v4 with: persist-credentials: false submodules: recursive #- name: Install sccache # run: | # cargo install sccache --version 0.4.0 --locked # echo "$HOME/.cargo/bin" >> $GITHUB_PATH - name: Install dist # we specify bash to get pipefail; it guards against the `curl` command # failing. otherwise `sh` won't catch that `curl` returned non-0 shell: bash run: "curl --proto '=https' --tlsv1.2 -LsSf https://github.com/axodotdev/cargo-dist/releases/download/v0.30.3/cargo-dist-installer.sh | sh" - name: Cache dist uses: actions/upload-artifact@v4 with: name: cargo-dist-cache path: ~/.cargo/bin/dist # sure would be cool if github gave us proper conditionals... # so here's a doubly-nested ternary-via-truthiness to try to provide the best possible # functionality based on whether this is a pull_request, and whether it's from a fork. # (PRs run on the *source* but secrets are usually on the *target* -- that's *good* # but also really annoying to build CI around when it needs secrets to work right.) - id: plan run: | dist ${{ (!github.event.pull_request && format('host --steps=create --tag={0}', github.ref_name)) || 'plan' }} --output-format=json > plan-dist-manifest.json echo "dist ran successfully" cat plan-dist-manifest.json echo "manifest=$(jq -c "." plan-dist-manifest.json)" >> "$GITHUB_OUTPUT" - name: "Upload dist-manifest.json" uses: actions/upload-artifact@v4 with: name: artifacts-plan-dist-manifest path: plan-dist-manifest.json # Build and packages all the platform-specific things build-local-artifacts: name: build-local-artifacts (${{ join(matrix.targets, ', ') }}) # Let the initial task tell us to not run (currently very blunt) needs: - plan if: ${{ fromJson(needs.plan.outputs.val).ci.github.artifacts_matrix.include != null && (needs.plan.outputs.publishing == 'true' || fromJson(needs.plan.outputs.val).ci.github.pr_run_mode == 'upload') }} strategy: fail-fast: false # Target platforms/runners are computed by dist in create-release. # Each member of the matrix has the following arguments: # # - runner: the github runner # - dist-args: cli flags to pass to dist # - install-dist: expression to run to install dist on the runner # # Typically there will be: # - 1 "global" task that builds universal installers # - N "local" tasks that build each platform's binaries and platform-specific installers matrix: ${{ fromJson(needs.plan.outputs.val).ci.github.artifacts_matrix }} runs-on: ${{ matrix.runner }} container: ${{ matrix.container && matrix.container.image || null }} env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} BUILD_MANIFEST_NAME: target/distrib/${{ join(matrix.targets, '-') }}-dist-manifest.json steps: - name: enable windows longpaths run: | git config --global core.longpaths true - uses: actions/checkout@v4 with: persist-credentials: false submodules: recursive #- name: Install sccache # run: | # cargo install sccache --version 0.4.0 --locked # echo "$HOME/.cargo/bin" >> $GITHUB_PATH - name: Install Rust non-interactively if not already installed if: ${{ matrix.container }} run: | if ! command -v cargo > /dev/null 2>&1; then curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y echo "$HOME/.cargo/bin" >> $GITHUB_PATH fi - name: Install dist run: ${{ matrix.install_dist.run }} # Get the dist-manifest - name: Fetch local artifacts uses: actions/download-artifact@v4 with: pattern: artifacts-* path: target/distrib/ merge-multiple: true - name: Install dependencies run: | ${{ matrix.packages_install }} - name: Build artifacts run: | # Actually do builds and make zips and whatnot dist build ${{ needs.plan.outputs.tag-flag }} --print=linkage --output-format=json ${{ matrix.dist_args }} > dist-manifest.json echo "dist ran successfully" - id: cargo-dist name: Post-build # We force bash here just because github makes it really hard to get values up # to "real" actions without writing to env-vars, and writing to env-vars has # inconsistent syntax between shell and powershell. shell: bash run: | # Parse out what we just built and upload it to scratch storage echo "paths<<EOF" >> "$GITHUB_OUTPUT" dist print-upload-files-from-manifest --manifest dist-manifest.json >> "$GITHUB_OUTPUT" echo "EOF" >> "$GITHUB_OUTPUT" cp dist-manifest.json "$BUILD_MANIFEST_NAME" - name: "Upload artifacts" uses: actions/upload-artifact@v4 with: name: artifacts-build-local-${{ join(matrix.targets, '_') }} path: | ${{ steps.cargo-dist.outputs.paths }} ${{ env.BUILD_MANIFEST_NAME }} # Build and package all the platform-agnostic(ish) things build-global-artifacts: needs: - plan - build-local-artifacts runs-on: "ubuntu-22.04" env: BUILD_MANIFEST_NAME: target/distrib/global-dist-manifest.json steps: - uses: actions/checkout@v4 with: persist-credentials: false submodules: recursive #- name: Install sccache # run: | # cargo install sccache --version 0.4.0 --locked # echo "$HOME/.cargo/bin" >> $GITHUB_PATH - name: Install cached dist uses: actions/download-artifact@v4 with: name: cargo-dist-cache path: ~/.cargo/bin/ - run: chmod +x ~/.cargo/bin/dist # Get all the local artifacts for the global tasks to use (for e.g. checksums) - name: Fetch local artifacts uses: actions/download-artifact@v4 with: pattern: artifacts-* path: target/distrib/ merge-multiple: true - id: cargo-dist shell: bash run: | dist build ${{ needs.plan.outputs.tag-flag }} --output-format=json "--artifacts=global" > dist-manifest.json echo "dist ran successfully" # Parse out what we just built and upload it to scratch storage echo "paths<<EOF" >> "$GITHUB_OUTPUT" jq --raw-output ".upload_files[]" dist-manifest.json >> "$GITHUB_OUTPUT" echo "EOF" >> "$GITHUB_OUTPUT" cp dist-manifest.json "$BUILD_MANIFEST_NAME" - name: "Upload artifacts" uses: actions/upload-artifact@v4 with: name: artifacts-build-global path: | ${{ steps.cargo-dist.outputs.paths }} ${{ env.BUILD_MANIFEST_NAME }} # Determines if we should publish/announce host: needs: - plan - build-local-artifacts - build-global-artifacts # Only run if we're "publishing", and only if plan, local and global didn't fail (skipped is fine) if: ${{ always() && needs.plan.result == 'success' && needs.plan.outputs.publishing == 'true' && (needs.build-global-artifacts.result == 'skipped' || needs.build-global-artifacts.result == 'success') && (needs.build-local-artifacts.result == 'skipped' || needs.build-local-artifacts.result == 'success') }} env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} runs-on: "ubuntu-22.04" outputs: val: ${{ steps.host.outputs.manifest }} steps: - uses: actions/checkout@v4 with: persist-credentials: false submodules: recursive #- name: Install sccache # run: | # cargo install sccache --version 0.4.0 --locked # echo "$HOME/.cargo/bin" >> $GITHUB_PATH - name: Install cached dist uses: actions/download-artifact@v4 with: name: cargo-dist-cache path: ~/.cargo/bin/ - run: chmod +x ~/.cargo/bin/dist # Fetch artifacts from scratch-storage - name: Fetch artifacts uses: actions/download-artifact@v4 with: pattern: artifacts-* path: target/distrib/ merge-multiple: true - id: host shell: bash run: | dist host ${{ needs.plan.outputs.tag-flag }} --steps=upload --steps=release --output-format=json > dist-manifest.json echo "artifacts uploaded and released successfully" cat dist-manifest.json echo "manifest=$(jq -c "." dist-manifest.json)" >> "$GITHUB_OUTPUT" - name: "Upload dist-manifest.json" uses: actions/upload-artifact@v4 with: # Overwrite the previous copy name: artifacts-dist-manifest path: dist-manifest.json # Create a GitHub Release while uploading all files to it - name: "Download GitHub Artifacts" uses: actions/download-artifact@v4 with: pattern: artifacts-* path: artifacts merge-multiple: true - name: Cleanup run: | # Remove the granular manifests rm -f artifacts/*-dist-manifest.json - name: Create GitHub Release env: PRERELEASE_FLAG: "${{ fromJson(steps.host.outputs.manifest).announcement_is_prerelease && '--prerelease' || '' }}" ANNOUNCEMENT_TITLE: "${{ fromJson(steps.host.outputs.manifest).announcement_title }}" ANNOUNCEMENT_BODY: "${{ fromJson(steps.host.outputs.manifest).announcement_github_body }}" RELEASE_COMMIT: "${{ github.sha }}" run: | # Write and read notes from a file to avoid quoting breaking things echo "$ANNOUNCEMENT_BODY" > $RUNNER_TEMP/notes.txt gh release create "${{ needs.plan.outputs.tag }}" --target "$RELEASE_COMMIT" $PRERELEASE_FLAG --title "$ANNOUNCEMENT_TITLE" --notes-file "$RUNNER_TEMP/notes.txt" artifacts/* publish-homebrew-formula: needs: - plan - host runs-on: "ubuntu-22.04" env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} PLAN: ${{ needs.plan.outputs.val }} GITHUB_USER: "axo bot" GITHUB_EMAIL: "admin+bot@axo.dev" if: ${{ !fromJson(needs.plan.outputs.val).announcement_is_prerelease || fromJson(needs.plan.outputs.val).publish_prereleases }} steps: - uses: actions/checkout@v4 with: persist-credentials: true repository: "gnostr-org/homebrew-gnostr" token: ${{ secrets.HOMEBREW_TAP_TOKEN }} # So we have access to the formula - name: Fetch homebrew formulae uses: actions/download-artifact@v4 with: pattern: artifacts-* path: Formula/ merge-multiple: true # This is extra complex because you can make your Formula name not match your app name # so we need to find releases with a *.rb file, and publish with that filename. - name: Commit formula files run: | git config --global user.name "${GITHUB_USER}" git config --global user.email "${GITHUB_EMAIL}" for release in $(echo "$PLAN" | jq --compact-output '.releases[] | select([.artifacts[] | endswith(".rb")] | any)'); do filename=$(echo "$release" | jq '.artifacts[] | select(endswith(".rb"))' --raw-output) name=$(echo "$filename" | sed "s/\.rb$//") version=$(echo "$release" | jq .app_version --raw-output) export PATH="/home/linuxbrew/.linuxbrew/bin:$PATH" brew update # We avoid reformatting user-provided data such as the app description and homepage. brew style --except-cops FormulaAudit/Homepage,FormulaAudit/Desc,FormulaAuditStrict --fix "Formula/${filename}" || true git add "Formula/${filename}" git commit -m "${name} ${version}" done git push origin HEAD announce: needs: - plan - host - publish-homebrew-formula # use "always() && ..." to allow us to wait for all publish jobs while # still allowing individual publish jobs to skip themselves (for prereleases). # "host" however must run to completion, no skipping allowed! if: ${{ always() && needs.host.result == 'success' && (needs.publish-homebrew-formula.result == 'skipped' || needs.publish-homebrew-formula.result == 'success') }} runs-on: "ubuntu-22.04" env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} steps: - uses: actions/checkout@v4 with: persist-credentials: false submodules: recursive
# This file was autogenerated by dist: # # Copyright 2022-2024, axodotdev # SPDX-License-Identifier: MIT or Apache-2.0 # # CI that: # # * checks for a Git Tag that looks like a release # * builds artifacts with dist (archives, installers, hashes) # * uploads those artifacts to temporary workflow zip # * on success, uploads the artifacts to a GitHub Release # # Note that the GitHub Release will be created with a generated # title/body based on your changelogs. name: Release permissions: "contents": "write" # This task will run whenever you push a git tag that looks like a version # like "1.0.0", "v0.1.0-prerelease.1", "my-app/0.1.0", "releases/v1.0.0", etc. # Various formats will be parsed into a VERSION and an optional PACKAGE_NAME, where # PACKAGE_NAME must be the name of a Cargo package in your workspace, and VERSION # must be a Cargo-style SemVer Version (must have at least major.minor.patch). # # If PACKAGE_NAME is specified, then the announcement will be for that # package (erroring out if it doesn't have the given version or isn't dist-able). # # If PACKAGE_NAME isn't specified, then the announcement will be for all # (dist-able) packages in the workspace with that version (this mode is # intended for workspaces with only one dist-able package, or with all dist-able # packages versioned/released in lockstep). # # If you push multiple tags at once, separate instances of this workflow will # spin up, creating an independent announcement for each one. However, GitHub # will hard limit this to 3 tags per commit, as it will assume more tags is a # mistake. # # If there's a prerelease-style suffix to the version, then the release(s) # will be marked as a prerelease. on: push: tags: - "**[0-9]+.[0-9]+.[0-9]+*" jobs: # Run 'dist plan' (or host) to determine what tasks we need to do plan: runs-on: "ubuntu-22.04" outputs: val: ${{ steps.plan.outputs.manifest }} tag: ${{ !github.event.pull_request && github.ref_name || '' }} tag-flag: ${{ !github.event.pull_request && format('--tag={0}', github.ref_name) || '' }} publishing: ${{ !github.event.pull_request }} env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} steps: - uses: actions/checkout@v4 with: persist-credentials: false submodules: recursive #- name: Install sccache # run: | # cargo install sccache --version 0.4.0 --locked # echo "$HOME/.cargo/bin" >> $GITHUB_PATH - name: Install dist # we specify bash to get pipefail; it guards against the `curl` command # failing. otherwise `sh` won't catch that `curl` returned non-0 shell: bash run: "curl --proto '=https' --tlsv1.2 -LsSf https://github.com/axodotdev/cargo-dist/releases/download/v0.30.3/cargo-dist-installer.sh | sh" - name: Cache dist uses: actions/upload-artifact@v4 with: name: cargo-dist-cache path: ~/.cargo/bin/dist # sure would be cool if github gave us proper conditionals... # so here's a doubly-nested ternary-via-truthiness to try to provide the best possible # functionality based on whether this is a pull_request, and whether it's from a fork. # (PRs run on the *source* but secrets are usually on the *target* -- that's *good* # but also really annoying to build CI around when it needs secrets to work right.) - id: plan run: | dist ${{ (!github.event.pull_request && format('host --steps=create --tag={0}', github.ref_name)) || 'plan' }} --output-format=json > plan-dist-manifest.json echo "dist ran successfully" cat plan-dist-manifest.json echo "manifest=$(jq -c "." plan-dist-manifest.json)" >> "$GITHUB_OUTPUT" - name: "Upload dist-manifest.json" uses: actions/upload-artifact@v4 with: name: artifacts-plan-dist-manifest path: plan-dist-manifest.json # Build and packages all the platform-specific things build-local-artifacts: name: build-local-artifacts (${{ join(matrix.targets, ', ') }}) # Let the initial task tell us to not run (currently very blunt) needs: - plan if: ${{ fromJson(needs.plan.outputs.val).ci.github.artifacts_matrix.include != null && (needs.plan.outputs.publishing == 'true' || fromJson(needs.plan.outputs.val).ci.github.pr_run_mode == 'upload') }} strategy: fail-fast: false # Target platforms/runners are computed by dist in create-release. # Each member of the matrix has the following arguments: # # - runner: the github runner # - dist-args: cli flags to pass to dist # - install-dist: expression to run to install dist on the runner # # Typically there will be: # - 1 "global" task that builds universal installers # - N "local" tasks that build each platform's binaries and platform-specific installers matrix: ${{ fromJson(needs.plan.outputs.val).ci.github.artifacts_matrix }} runs-on: ${{ matrix.runner }} container: ${{ matrix.container && matrix.container.image || null }} env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} BUILD_MANIFEST_NAME: target/distrib/${{ join(matrix.targets, '-') }}-dist-manifest.json steps: - name: enable windows longpaths run: | git config --global core.longpaths true - uses: actions/checkout@v4 with: persist-credentials: false submodules: recursive #- name: Install sccache # run: | # cargo install sccache --version 0.4.0 --locked # echo "$HOME/.cargo/bin" >> $GITHUB_PATH - name: Install Rust non-interactively if not already installed if: ${{ matrix.container }} run: | if ! command -v cargo > /dev/null 2>&1; then curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y echo "$HOME/.cargo/bin" >> $GITHUB_PATH fi - name: Install dist run: ${{ matrix.install_dist.run }} # Get the dist-manifest - name: Fetch local artifacts uses: actions/download-artifact@v4 with: pattern: artifacts-* path: target/distrib/ merge-multiple: true - name: Install dependencies run: | ${{ matrix.packages_install }} - name: Build artifacts run: | # Actually do builds and make zips and whatnot dist build ${{ needs.plan.outputs.tag-flag }} --print=linkage --output-format=json ${{ matrix.dist_args }} > dist-manifest.json echo "dist ran successfully" - id: cargo-dist name: Post-build # We force bash here just because github makes it really hard to get values up # to "real" actions without writing to env-vars, and writing to env-vars has # inconsistent syntax between shell and powershell. shell: bash run: | # Parse out what we just built and upload it to scratch storage echo "paths<<EOF" >> "$GITHUB_OUTPUT" dist print-upload-files-from-manifest --manifest dist-manifest.json >> "$GITHUB_OUTPUT" echo "EOF" >> "$GITHUB_OUTPUT" cp dist-manifest.json "$BUILD_MANIFEST_NAME" - name: "Upload artifacts" uses: actions/upload-artifact@v4 with: name: artifacts-build-local-${{ join(matrix.targets, '_') }} path: | ${{ steps.cargo-dist.outputs.paths }} ${{ env.BUILD_MANIFEST_NAME }} # Build and package all the platform-agnostic(ish) things build-global-artifacts: needs: - plan - build-local-artifacts runs-on: "ubuntu-22.04" env: BUILD_MANIFEST_NAME: target/distrib/global-dist-manifest.json steps: - uses: actions/checkout@v4 with: persist-credentials: false submodules: recursive #- name: Install sccache # run: | # cargo install sccache --version 0.4.0 --locked # echo "$HOME/.cargo/bin" >> $GITHUB_PATH - name: Install cached dist uses: actions/download-artifact@v4 with: name: cargo-dist-cache path: ~/.cargo/bin/ - run: chmod +x ~/.cargo/bin/dist # Get all the local artifacts for the global tasks to use (for e.g. checksums) - name: Fetch local artifacts uses: actions/download-artifact@v4 with: pattern: artifacts-* path: target/distrib/ merge-multiple: true - id: cargo-dist shell: bash run: | dist build ${{ needs.plan.outputs.tag-flag }} --output-format=json "--artifacts=global" > dist-manifest.json echo "dist ran successfully" # Parse out what we just built and upload it to scratch storage echo "paths<<EOF" >> "$GITHUB_OUTPUT" jq --raw-output ".upload_files[]" dist-manifest.json >> "$GITHUB_OUTPUT" echo "EOF" >> "$GITHUB_OUTPUT" cp dist-manifest.json "$BUILD_MANIFEST_NAME" - name: "Upload artifacts" uses: actions/upload-artifact@v4 with: name: artifacts-build-global path: | ${{ steps.cargo-dist.outputs.paths }} ${{ env.BUILD_MANIFEST_NAME }} # Determines if we should publish/announce host: needs: - plan - build-local-artifacts - build-global-artifacts # Only run if we're "publishing", and only if plan, local and global didn't fail (skipped is fine) if: ${{ always() && needs.plan.result == 'success' && needs.plan.outputs.publishing == 'true' && (needs.build-global-artifacts.result == 'skipped' || needs.build-global-artifacts.result == 'success') && (needs.build-local-artifacts.result == 'skipped' || needs.build-local-artifacts.result == 'success') }} env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} runs-on: "ubuntu-22.04" outputs: val: ${{ steps.host.outputs.manifest }} steps: - uses: actions/checkout@v4 with: persist-credentials: false submodules: recursive #- name: Install sccache # run: | # cargo install sccache --version 0.4.0 --locked # echo "$HOME/.cargo/bin" >> $GITHUB_PATH - name: Install cached dist uses: actions/download-artifact@v4 with: name: cargo-dist-cache path: ~/.cargo/bin/ - run: chmod +x ~/.cargo/bin/dist # Fetch artifacts from scratch-storage - name: Fetch artifacts uses: actions/download-artifact@v4 with: pattern: artifacts-* path: target/distrib/ merge-multiple: true - id: host shell: bash run: | dist host ${{ needs.plan.outputs.tag-flag }} --steps=upload --steps=release --output-format=json > dist-manifest.json echo "artifacts uploaded and released successfully" cat dist-manifest.json echo "manifest=$(jq -c "." dist-manifest.json)" >> "$GITHUB_OUTPUT" - name: "Upload dist-manifest.json" uses: actions/upload-artifact@v4 with: # Overwrite the previous copy name: artifacts-dist-manifest path: dist-manifest.json # Create a GitHub Release while uploading all files to it - name: "Download GitHub Artifacts" uses: actions/download-artifact@v4 with: pattern: artifacts-* path: artifacts merge-multiple: true - name: Cleanup run: | # Remove the granular manifests rm -f artifacts/*-dist-manifest.json - name: Create GitHub Release env: PRERELEASE_FLAG: "${{ fromJson(steps.host.outputs.manifest).announcement_is_prerelease && '--prerelease' || '' }}" ANNOUNCEMENT_TITLE: "${{ fromJson(steps.host.outputs.manifest).announcement_title }}" ANNOUNCEMENT_BODY: "${{ fromJson(steps.host.outputs.manifest).announcement_github_body }}" RELEASE_COMMIT: "${{ github.sha }}" run: | # Write and read notes from a file to avoid quoting breaking things echo "$ANNOUNCEMENT_BODY" > $RUNNER_TEMP/notes.txt gh release create "${{ needs.plan.outputs.tag }}" --target "$RELEASE_COMMIT" $PRERELEASE_FLAG --title "$ANNOUNCEMENT_TITLE" --notes-file "$RUNNER_TEMP/notes.txt" artifacts/* publish-homebrew-formula: needs: - plan - host runs-on: "ubuntu-22.04" env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} PLAN: ${{ needs.plan.outputs.val }} GITHUB_USER: "axo bot" GITHUB_EMAIL: "admin+bot@axo.dev" if: ${{ !fromJson(needs.plan.outputs.val).announcement_is_prerelease || fromJson(needs.plan.outputs.val).publish_prereleases }} steps: - uses: actions/checkout@v4 with: persist-credentials: true repository: "gnostr-org/homebrew-gnostr" token: ${{ secrets.HOMEBREW_TAP_TOKEN }} # So we have access to the formula - name: Fetch homebrew formulae uses: actions/download-artifact@v4 with: pattern: artifacts-* path: Formula/ merge-multiple: true # This is extra complex because you can make your Formula name not match your app name # so we need to find releases with a *.rb file, and publish with that filename. - name: Commit formula files run: | git config --global user.name "${GITHUB_USER}" git config --global user.email "${GITHUB_EMAIL}" for release in $(echo "$PLAN" | jq --compact-output '.releases[] | select([.artifacts[] | endswith(".rb")] | any)'); do filename=$(echo "$release" | jq '.artifacts[] | select(endswith(".rb"))' --raw-output) name=$(echo "$filename" | sed "s/\.rb$//") version=$(echo "$release" | jq .app_version --raw-output) export PATH="/home/linuxbrew/.linuxbrew/bin:$PATH" brew update # We avoid reformatting user-provided data such as the app description and homepage. brew style --except-cops FormulaAudit/Homepage,FormulaAudit/Desc,FormulaAuditStrict --fix "Formula/${filename}" || true git add "Formula/${filename}" git commit -m "${name} ${version}" done git push origin HEAD announce: needs: - plan - host - publish-homebrew-formula # use "always() && ..." to allow us to wait for all publish jobs while # still allowing individual publish jobs to skip themselves (for prereleases). # "host" however must run to completion, no skipping allowed! if: ${{ always() && needs.host.result == 'success' && (needs.publish-homebrew-formula.result == 'skipped' || needs.publish-homebrew-formula.result == 'success') }} runs-on: "ubuntu-22.04" env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} steps: - uses: actions/checkout@v4 with: persist-credentials: false submodules: recursive
name: Rust on: push: branches: [ "*" ] pull_request: branches: [ "*" ] env: CARGO_TERM_COLOR: always FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true RUST_LOG: info jobs: build: runs-on: ${{ matrix.os }} strategy: matrix: os: [ubuntu-latest, macos-15-intel, macos-latest, windows-latest] features_args: ["", "--no-default-features", "--features nostr"] steps: - uses: actions/checkout@v4 - name: Build ${{ matrix.features_args }} run: cargo build --workspace --verbose ${{ matrix.features_args }} - name: Run workspace tests ${{ matrix.features_args }} run: | cargo test --workspace ${{ matrix.features_args }} -- --test-threads 1 - name: Run get_file_hash_core tests ${{ matrix.features_args }} shell: bash run: | if [[ "${{ matrix.features_args }}" == "--features nostr" ]]; then cargo test -p get_file_hash_core ${{ matrix.features_args }} -- --test-threads 1 --nocapture else cargo test -p get_file_hash_core ${{ matrix.features_args }} -- --test-threads 1 fi - name: Run get_file_hash tests ${{ matrix.features_args }} shell: bash run: | if [[ "${{ matrix.features_args }}" == "--features nostr" ]]; then cargo test -p get_file_hash ${{ matrix.features_args }} -- --test-threads 1 --nocapture else cargo test -p get_file_hash ${{ matrix.features_args }} -- --test-threads 1 fi - name: Build Release ${{ matrix.features_args }} run: cargo build --workspace --release ${{ matrix.features_args }}
# This file was autogenerated by dist: # # Copyright 2022-2024, axodotdev # SPDX-License-Identifier: MIT or Apache-2.0 # # CI that: # # * checks for a Git Tag that looks like a release # * builds artifacts with dist (archives, installers, hashes) # * uploads those artifacts to temporary workflow zip # * on success, uploads the artifacts to a GitHub Release # # Note that the GitHub Release will be created with a generated # title/body based on your changelogs. name: Release permissions: "contents": "write" # This task will run whenever you push a git tag that looks like a version # like "1.0.0", "v0.1.0-prerelease.1", "my-app/0.1.0", "releases/v1.0.0", etc. # Various formats will be parsed into a VERSION and an optional PACKAGE_NAME, where # PACKAGE_NAME must be the name of a Cargo package in your workspace, and VERSION # must be a Cargo-style SemVer Version (must have at least major.minor.patch). # # If PACKAGE_NAME is specified, then the announcement will be for that # package (erroring out if it doesn't have the given version or isn't dist-able). # # If PACKAGE_NAME isn't specified, then the announcement will be for all # (dist-able) packages in the workspace with that version (this mode is # intended for workspaces with only one dist-able package, or with all dist-able # packages versioned/released in lockstep). # # If you push multiple tags at once, separate instances of this workflow will # spin up, creating an independent announcement for each one. However, GitHub # will hard limit this to 3 tags per commit, as it will assume more tags is a # mistake. # # If there's a prerelease-style suffix to the version, then the release(s) # will be marked as a prerelease. on: push: tags: - "**[0-9]+.[0-9]+.[0-9]+*" jobs: # Run 'dist plan' (or host) to determine what tasks we need to do plan: runs-on: "ubuntu-22.04" outputs: val: ${{ steps.plan.outputs.manifest }} tag: ${{ !github.event.pull_request && github.ref_name || '' }} tag-flag: ${{ !github.event.pull_request && format('--tag={0}', github.ref_name) || '' }} publishing: ${{ !github.event.pull_request }} env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} steps: - uses: actions/checkout@v4 with: persist-credentials: false submodules: recursive #- name: Install sccache # run: | # cargo install sccache --version 0.4.0 --locked # echo "$HOME/.cargo/bin" >> $GITHUB_PATH - name: Install dist # we specify bash to get pipefail; it guards against the `curl` command # failing. otherwise `sh` won't catch that `curl` returned non-0 shell: bash run: "curl --proto '=https' --tlsv1.2 -LsSf https://github.com/axodotdev/cargo-dist/releases/download/v0.30.3/cargo-dist-installer.sh | sh" - name: Cache dist uses: actions/upload-artifact@v4 with: name: cargo-dist-cache path: ~/.cargo/bin/dist # sure would be cool if github gave us proper conditionals... # so here's a doubly-nested ternary-via-truthiness to try to provide the best possible # functionality based on whether this is a pull_request, and whether it's from a fork. # (PRs run on the *source* but secrets are usually on the *target* -- that's *good* # but also really annoying to build CI around when it needs secrets to work right.) - id: plan run: | dist ${{ (!github.event.pull_request && format('host --steps=create --tag={0}', github.ref_name)) || 'plan' }} --output-format=json > plan-dist-manifest.json echo "dist ran successfully" cat plan-dist-manifest.json echo "manifest=$(jq -c "." plan-dist-manifest.json)" >> "$GITHUB_OUTPUT" - name: "Upload dist-manifest.json" uses: actions/upload-artifact@v4 with: name: artifacts-plan-dist-manifest path: plan-dist-manifest.json # Build and packages all the platform-specific things build-local-artifacts: name: build-local-artifacts (${{ join(matrix.targets, ', ') }}) # Let the initial task tell us to not run (currently very blunt) needs: - plan if: ${{ fromJson(needs.plan.outputs.val).ci.github.artifacts_matrix.include != null && (needs.plan.outputs.publishing == 'true' || fromJson(needs.plan.outputs.val).ci.github.pr_run_mode == 'upload') }} strategy: fail-fast: false # Target platforms/runners are computed by dist in create-release. # Each member of the matrix has the following arguments: # # - runner: the github runner # - dist-args: cli flags to pass to dist # - install-dist: expression to run to install dist on the runner # # Typically there will be: # - 1 "global" task that builds universal installers # - N "local" tasks that build each platform's binaries and platform-specific installers matrix: ${{ fromJson(needs.plan.outputs.val).ci.github.artifacts_matrix }} runs-on: ${{ matrix.runner }} container: ${{ matrix.container && matrix.container.image || null }} env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} BUILD_MANIFEST_NAME: target/distrib/${{ join(matrix.targets, '-') }}-dist-manifest.json steps: - name: enable windows longpaths run: | git config --global core.longpaths true - uses: actions/checkout@v4 with: persist-credentials: false submodules: recursive #- name: Install sccache # run: | # cargo install sccache --version 0.4.0 --locked # echo "$HOME/.cargo/bin" >> $GITHUB_PATH - name: Install Rust non-interactively if not already installed if: ${{ matrix.container }} run: | if ! command -v cargo > /dev/null 2>&1; then curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y echo "$HOME/.cargo/bin" >> $GITHUB_PATH fi - name: Install dist run: ${{ matrix.install_dist.run }} # Get the dist-manifest - name: Fetch local artifacts uses: actions/download-artifact@v4 with: pattern: artifacts-* path: target/distrib/ merge-multiple: true - name: Install dependencies run: | ${{ matrix.packages_install }} - name: Build artifacts run: | # Actually do builds and make zips and whatnot dist build ${{ needs.plan.outputs.tag-flag }} --print=linkage --output-format=json ${{ matrix.dist_args }} > dist-manifest.json echo "dist ran successfully" - id: cargo-dist name: Post-build # We force bash here just because github makes it really hard to get values up # to "real" actions without writing to env-vars, and writing to env-vars has # inconsistent syntax between shell and powershell. shell: bash run: | # Parse out what we just built and upload it to scratch storage echo "paths<<EOF" >> "$GITHUB_OUTPUT" dist print-upload-files-from-manifest --manifest dist-manifest.json >> "$GITHUB_OUTPUT" echo "EOF" >> "$GITHUB_OUTPUT" cp dist-manifest.json "$BUILD_MANIFEST_NAME" - name: "Upload artifacts" uses: actions/upload-artifact@v4 with: name: artifacts-build-local-${{ join(matrix.targets, '_') }} path: | ${{ steps.cargo-dist.outputs.paths }} ${{ env.BUILD_MANIFEST_NAME }} # Build and package all the platform-agnostic(ish) things build-global-artifacts: needs: - plan - build-local-artifacts runs-on: "ubuntu-22.04" env: BUILD_MANIFEST_NAME: target/distrib/global-dist-manifest.json steps: - uses: actions/checkout@v4 with: persist-credentials: false submodules: recursive #- name: Install sccache # run: | # cargo install sccache --version 0.4.0 --locked # echo "$HOME/.cargo/bin" >> $GITHUB_PATH - name: Install cached dist uses: actions/download-artifact@v4 with: name: cargo-dist-cache path: ~/.cargo/bin/ - run: chmod +x ~/.cargo/bin/dist # Get all the local artifacts for the global tasks to use (for e.g. checksums) - name: Fetch local artifacts uses: actions/download-artifact@v4 with: pattern: artifacts-* path: target/distrib/ merge-multiple: true - id: cargo-dist shell: bash run: | dist build ${{ needs.plan.outputs.tag-flag }} --output-format=json "--artifacts=global" > dist-manifest.json echo "dist ran successfully" # Parse out what we just built and upload it to scratch storage echo "paths<<EOF" >> "$GITHUB_OUTPUT" jq --raw-output ".upload_files[]" dist-manifest.json >> "$GITHUB_OUTPUT" echo "EOF" >> "$GITHUB_OUTPUT" cp dist-manifest.json "$BUILD_MANIFEST_NAME" - name: "Upload artifacts" uses: actions/upload-artifact@v4 with: name: artifacts-build-global path: | ${{ steps.cargo-dist.outputs.paths }} ${{ env.BUILD_MANIFEST_NAME }} # Determines if we should publish/announce host: needs: - plan - build-local-artifacts - build-global-artifacts # Only run if we're "publishing", and only if plan, local and global didn't fail (skipped is fine) if: ${{ always() && needs.plan.result == 'success' && needs.plan.outputs.publishing == 'true' && (needs.build-global-artifacts.result == 'skipped' || needs.build-global-artifacts.result == 'success') && (needs.build-local-artifacts.result == 'skipped' || needs.build-local-artifacts.result == 'success') }} env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} runs-on: "ubuntu-22.04" outputs: val: ${{ steps.host.outputs.manifest }} steps: - uses: actions/checkout@v4 with: persist-credentials: false submodules: recursive #- name: Install sccache # run: | # cargo install sccache --version 0.4.0 --locked # echo "$HOME/.cargo/bin" >> $GITHUB_PATH - name: Install cached dist uses: actions/download-artifact@v4 with: name: cargo-dist-cache path: ~/.cargo/bin/ - run: chmod +x ~/.cargo/bin/dist # Fetch artifacts from scratch-storage - name: Fetch artifacts uses: actions/download-artifact@v4 with: pattern: artifacts-* path: target/distrib/ merge-multiple: true - id: host shell: bash run: | dist host ${{ needs.plan.outputs.tag-flag }} --steps=upload --steps=release --output-format=json > dist-manifest.json echo "artifacts uploaded and released successfully" cat dist-manifest.json echo "manifest=$(jq -c "." dist-manifest.json)" >> "$GITHUB_OUTPUT" - name: "Upload dist-manifest.json" uses: actions/upload-artifact@v4 with: # Overwrite the previous copy name: artifacts-dist-manifest path: dist-manifest.json # Create a GitHub Release while uploading all files to it - name: "Download GitHub Artifacts" uses: actions/download-artifact@v4 with: pattern: artifacts-* path: artifacts merge-multiple: true - name: Cleanup run: | # Remove the granular manifests rm -f artifacts/*-dist-manifest.json - name: Create GitHub Release env: PRERELEASE_FLAG: "${{ fromJson(steps.host.outputs.manifest).announcement_is_prerelease && '--prerelease' || '' }}" ANNOUNCEMENT_TITLE: "${{ fromJson(steps.host.outputs.manifest).announcement_title }}" ANNOUNCEMENT_BODY: "${{ fromJson(steps.host.outputs.manifest).announcement_github_body }}" RELEASE_COMMIT: "${{ github.sha }}" run: | # Write and read notes from a file to avoid quoting breaking things echo "$ANNOUNCEMENT_BODY" > $RUNNER_TEMP/notes.txt gh release create "${{ needs.plan.outputs.tag }}" --target "$RELEASE_COMMIT" $PRERELEASE_FLAG --title "$ANNOUNCEMENT_TITLE" --notes-file "$RUNNER_TEMP/notes.txt" artifacts/* publish-homebrew-formula: needs: - plan - host runs-on: "ubuntu-22.04" env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} PLAN: ${{ needs.plan.outputs.val }} GITHUB_USER: "axo bot" GITHUB_EMAIL: "admin+bot@axo.dev" if: ${{ !fromJson(needs.plan.outputs.val).announcement_is_prerelease || fromJson(needs.plan.outputs.val).publish_prereleases }} steps: - uses: actions/checkout@v4 with: persist-credentials: true repository: "gnostr-org/homebrew-gnostr" token: ${{ secrets.HOMEBREW_TAP_TOKEN }} # So we have access to the formula - name: Fetch homebrew formulae uses: actions/download-artifact@v4 with: pattern: artifacts-* path: Formula/ merge-multiple: true # This is extra complex because you can make your Formula name not match your app name # so we need to find releases with a *.rb file, and publish with that filename. - name: Commit formula files run: | git config --global user.name "${GITHUB_USER}" git config --global user.email "${GITHUB_EMAIL}" for release in $(echo "$PLAN" | jq --compact-output '.releases[] | select([.artifacts[] | endswith(".rb")] | any)'); do filename=$(echo "$release" | jq '.artifacts[] | select(endswith(".rb"))' --raw-output) name=$(echo "$filename" | sed "s/\.rb$//") version=$(echo "$release" | jq .app_version --raw-output) export PATH="/home/linuxbrew/.linuxbrew/bin:$PATH" brew update # We avoid reformatting user-provided data such as the app description and homepage. brew style --except-cops FormulaAudit/Homepage,FormulaAudit/Desc,FormulaAuditStrict --fix "Formula/${filename}" || true git add "Formula/${filename}" git commit -m "${name} ${version}" done git push origin HEAD announce: needs: - plan - host - publish-homebrew-formula # use "always() && ..." to allow us to wait for all publish jobs while # still allowing individual publish jobs to skip themselves (for prereleases). # "host" however must run to completion, no skipping allowed! if: ${{ always() && needs.host.result == 'success' && (needs.publish-homebrew-formula.result == 'skipped' || needs.publish-homebrew-formula.result == 'success') }} runs-on: "ubuntu-22.04" env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} steps: - uses: actions/checkout@v4 with: persist-credentials: false submodules: recursive
name: Rust on: push: branches: [ "*" ] pull_request: branches: [ "*" ] env: CARGO_TERM_COLOR: always FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true RUST_LOG: info jobs: build: runs-on: ${{ matrix.os }} strategy: matrix: os: [ubuntu-latest, macos-15-intel, macos-latest, windows-latest] features_args: ["", "--no-default-features", "--features nostr"] steps: - uses: actions/checkout@v4 - name: Build ${{ matrix.features_args }} run: cargo build --workspace --verbose ${{ matrix.features_args }} - name: Run workspace tests ${{ matrix.features_args }} run: | cargo test --workspace ${{ matrix.features_args }} -- --test-threads 1 - name: Run get_file_hash_core tests ${{ matrix.features_args }} shell: bash run: | if [[ "${{ matrix.features_args }}" == "--features nostr" ]]; then cargo test -p get_file_hash_core ${{ matrix.features_args }} -- --test-threads 1 --nocapture else cargo test -p get_file_hash_core ${{ matrix.features_args }} -- --test-threads 1 fi - name: Run get_file_hash tests ${{ matrix.features_args }} shell: bash run: | if [[ "${{ matrix.features_args }}" == "--features nostr" ]]; then cargo test -p get_file_hash ${{ matrix.features_args }} -- --test-threads 1 --nocapture else cargo test -p get_file_hash ${{ matrix.features_args }} -- --test-threads 1 fi - name: Build Release ${{ matrix.features_args }} run: cargo build --workspace --release ${{ matrix.features_args }}