diff --git a/.github/workflows/build_b3sum.py b/.github/workflows/build_b3sum.py new file mode 100644 index 0000000..e487daf --- /dev/null +++ b/.github/workflows/build_b3sum.py @@ -0,0 +1,37 @@ +#! /usr/bin/env python3 + +from pathlib import Path +import platform +import shutil +import subprocess +import sys + +ROOT = Path(__file__).parent.parent.parent +RUST_TARGET = sys.argv[1] + +subprocess.run(["cargo", "build", "--target", sys.argv[1], "--release"], + cwd=ROOT / "b3sum") + +if platform.system() == "Windows": + original_exe_name = "b3sum.exe" +else: + original_exe_name = "b3sum" + +if platform.system() == "Windows": + new_exe_name = "b3sum_windows_x64_bin.exe" +elif platform.system() == "Darwin": + new_exe_name = "b3sum_macos_x64_bin" +elif platform.system() == "Linux": + new_exe_name = "b3sum_linux_x64_bin" +else: + raise RuntimeError("Unexpected platform: " + platform.system()) + +# Copy the built binary so that it has the upload name we want. +out_dir = ROOT / "b3sum/target" / RUST_TARGET / "release" +original_exe_path = str(out_dir / original_exe_name) +new_exe_path = str(out_dir / new_exe_name) +print("copying", repr(original_exe_path), "to", repr(new_exe_path)) +shutil.copyfile(original_exe_path, new_exe_path) + +# This lets the subsequent upload step get the filepath. +print("::set-output name=bin_path::" + new_exe_path) diff --git a/.github/workflows/tag.yml b/.github/workflows/tag.yml new file mode 100644 index 0000000..577d4f3 --- /dev/null +++ b/.github/workflows/tag.yml @@ -0,0 +1,45 @@ +name: publish_b3sum_binaries + +on: + push: + tags: + - "*" + +env: + BLAKE3_CI: "1" + RUSTFLAGS: "-D warnings" + +jobs: + cargo_tests: + name: ${{ matrix.target.name }} + runs-on: ${{ matrix.target.os }} + strategy: + fail-fast: false + matrix: + target: [ + { "os": "ubuntu-latest", "rust-target": "x86_64-unknown-linux-musl", "name": "Linux" }, + { "os": "macOS-latest", "rust-target": "x86_64-apple-darwin", "name": "macOS" }, + { "os": "windows-latest", "rust-target": "x86_64-pc-windows-msvc", "name": "Windows" }, + ] + + steps: + - uses: actions/checkout@v1 + - uses: actions/setup-python@v1 + with: + python-version: "3.x" + - run: pip install PyGithub + - run: sudo apt-get install musl-tools + if: matrix.target.os == 'ubuntu-latest' + - uses: actions-rs/toolchain@v1 + with: + toolchain: stable + profile: minimal + - run: rustup target add ${{ matrix.target.rust-target }} + - name: build b3sum + id: build_b3sum + run: python -u .github/workflows/build_b3sum.py ${{ matrix.target.rust-target }} + - name: upload release asset + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GITHUB_TAG: ${{ github.ref }} + run: python -u .github/workflows/upload_github_release_asset.py ${{ steps.build_b3sum.outputs.bin_path }} diff --git a/.github/workflows/upload_github_release_asset.py b/.github/workflows/upload_github_release_asset.py new file mode 100755 index 0000000..c1cbf51 --- /dev/null +++ b/.github/workflows/upload_github_release_asset.py @@ -0,0 +1,65 @@ +#! /usr/bin/env python3 + +import github +import os +import sys + +RETRIES = 10 + +g = github.Github(os.environ["GITHUB_TOKEN"]) +tag_name = os.environ["GITHUB_TAG"] +tag_prefix = "refs/tags/" +if tag_name.startswith(tag_prefix): + tag_name = tag_name[len(tag_prefix):] +assert len(sys.argv) == 2 +asset_path = sys.argv[1] +asset_name = os.path.basename(asset_path) + +repo = g.get_repo(os.environ["GITHUB_REPOSITORY"]) + +tags = list(repo.get_tags()) + +for tag in tags: + if tag.name == tag_name: + break +else: + raise RuntimeError("no tag named " + repr(tag_name)) + +try: + print("Creating GitHub release for tag " + repr(tag_name) + "...") + repo.create_git_release(tag_name, tag_name, tag.commit.commit.message) +except github.GithubException as github_error: + if github_error.data["errors"][0]["code"] == "already_exists": + print("Release for tag " + repr(tag_name) + " already exists.") + else: + raise + +releases = list(repo.get_releases()) +for release in releases: + if release.tag_name == tag_name: + break +else: + raise RuntimeError("no release for tag " + repr(tag_name)) + +print("Uploading " + repr(asset_path) + "...") +for i in range(RETRIES): + try: + print("Upload attempt #{} of {}...".format(i + 1, RETRIES)) + release.upload_asset(asset_path) + break + except github.GithubException as github_error: + # Unfortunately the asset upload API is flaky. Even worse, it often + # partially succeeds, returning an error to the caller but leaving the + # release in a state where subsequent uploads of the same asset will + # fail with an "already_exists" error. (Though the asset is not visible + # on github.com, so we can't just declare victory and move on.) If we + # detect this case, explicitly delete the asset and continue retrying. + print(github_error) + for asset in release.get_assets(): + if asset.name == asset_name: + print("Found uploaded asset after failure. Deleting...") + asset.delete_asset() +else: + raise RuntimeError("All upload attempts failed.") + +print("Success!") diff --git a/README.md b/README.md index 2f6d04d..0bac8e1 100644 --- a/README.md +++ b/README.md @@ -76,11 +76,14 @@ we recommend [Argon2](https://github.com/P-H-C/phc-winner-argon2).* ### The `b3sum` utility -The `b3sum` utility allows you to process files and data from standard -input using BLAKE3 in any of its three modes. -To use `b3sum` on the command line, [install Rust and +The `b3sum` command line utility prints the BLAKE3 hashes of files or of +standard input. Prebuilt binaries are available for Linux, Windows, and +macOS (requiring the [unidentified developer +workaround](https://support.apple.com/guide/mac-help/open-a-mac-app-from-an-unidentified-developer-mh40616/mac)) +on the [releases page](https://github.com/BLAKE3-team/BLAKE3/releases). +If you've [installed Rust and Cargo](https://doc.rust-lang.org/cargo/getting-started/installation.html), -and then run: +you can also build `b3sum` yourself with: ```bash cargo install b3sum @@ -89,7 +92,7 @@ cargo install b3sum If `rustup` didn't configure your `PATH` for you, you might need to go looking for the installed binary in e.g. `~/.cargo/bin`. You can test out how fast BLAKE3 is on your machine by creating a big file and -hashing it, for example as follows: +hashing it, for example: ```bash # Create a 1 GB file.