-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #8 from multiversx/reproducible-builds-24
Add reusable workflow for reproducible builds
- Loading branch information
Showing
3 changed files
with
276 additions
and
670 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,170 @@ | ||
name: Build contracts deterministically | ||
|
||
on: | ||
workflow_call: | ||
inputs: | ||
image_tag: | ||
type: string | ||
required: true | ||
description: Image multiversx/sdk-rust-contract-builder | ||
contract_name: | ||
type: string | ||
required: false | ||
description: A specific contract to be built | ||
create_release: | ||
type: boolean | ||
description: Whether to create a new release | ||
attach_to_existing_release: | ||
type: boolean | ||
description: Whether to attach output to existing release | ||
|
||
jobs: | ||
build: | ||
runs-on: ubuntu-latest | ||
steps: | ||
- name: Handle input | ||
run: | | ||
python3 - << "EOF" | ||
import logging | ||
import os | ||
import sys | ||
logging.basicConfig(level=logging.INFO) | ||
image_tag = os.getenv("image_tag", "") | ||
contract_name = os.getenv("contract_name", "") | ||
create_release = os.getenv("create_release", "").lower() == "true" | ||
attach_to_existing_release = os.getenv("attach_to_existing_release", "").lower() == "true" | ||
logging.info(f"Image tag: {image_tag}") | ||
logging.info(f"Contract name: {contract_name or '*'}") | ||
logging.info(f"Create new release: {create_release}") | ||
logging.info(f"Attach output to existing release: {attach_to_existing_release}") | ||
if image_tag == "next": | ||
logging.warning("You've chosen to build the contracts with the image 'multiversx/sdk-rust-contract-builder:next', which is experimental and not recommended for production!") | ||
if contract_name: | ||
logging.info(f"One contract will be built: {contract_name}.") | ||
if create_release and attach_to_existing_release: | ||
sys.exit("ERROR: Choose either to create a new release, or to attach the build output to an existing release. Can't pick both. Feel free to pick none, though.") | ||
EOF | ||
env: | ||
image_tag: ${{ inputs.image_tag }} | ||
contract_name: ${{ inputs.contract_name }} | ||
create_release: ${{ inputs.create_release }} | ||
attach_to_existing_release: ${{ inputs.attach_to_existing_release }} | ||
|
||
- name: Checkout | ||
uses: actions/checkout@v3 | ||
with: | ||
ref: ${{ env.GITHUB_REF_NAME }} | ||
fetch-depth: 0 | ||
repository: ${{ env.GITHUB_REPOSITORY }} | ||
|
||
- name: Preliminary checks | ||
run: | | ||
python3 - << "EOF" | ||
import logging | ||
from pathlib import Path | ||
import sys | ||
logging.basicConfig(level=logging.INFO) | ||
logging.info("Check for missing 'wasm/Cargo.lock' files ...") | ||
missing_cargo_lock = False | ||
for cargo_toml in Path(".").rglob("wasm/Cargo.toml"): | ||
cargo_lock = cargo_toml.with_suffix(".lock") | ||
if cargo_lock.exists(): | ||
logging.info(f"Found wasm/Cargo.lock: {cargo_lock}") | ||
else: | ||
logging.error(f"wasm/Cargo.lock file not found: {cargo_lock}") | ||
missing_cargo_lock = True | ||
if missing_cargo_lock: | ||
sys.exit(f"ERROR: One or more 'wasm/Cargo.lock' files are missing. They are essential for reproducible builds.") | ||
EOF | ||
- name: Download build script | ||
run: | | ||
wget https://raw.githubusercontent.com/multiversx/mx-sdk-rust-contract-builder/${{ inputs.image_tag }}/build_with_docker.py | ||
- name: Build contracts | ||
run: | | ||
python3 ./build_with_docker.py --no-docker-tty --image=multiversx/sdk-rust-contract-builder:${{ inputs.image_tag }} --project=. --contract=${{ inputs.contract_name }} --output=/home/runner/work/output-from-docker | ||
- name: Save artifacts | ||
uses: actions/upload-artifact@v3 | ||
with: | ||
name: build-output | ||
path: | | ||
/home/runner/work/output-from-docker/**/*.* | ||
if-no-files-found: error | ||
|
||
- name: Create new release (if applicable) | ||
if: ${{ inputs.create_release == true }} | ||
run: | | ||
gh release create ${{ github.ref_name }} --target=$GITHUB_SHA --prerelease --title="New release: ${{ github.ref_name }}" --generate-notes | ||
# Give some time for processing the request | ||
sleep 10 | ||
env: | ||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||
image_tag: ${{ inputs.image_tag }} | ||
|
||
- name: Create release notes | ||
if: ${{ inputs.attach_to_existing_release == true || inputs.create_release == true }} | ||
run: | | ||
python3 - << "EOF" | ||
import logging | ||
from hashlib import blake2b | ||
from pathlib import Path | ||
import os | ||
import json | ||
import urllib.request | ||
logging.basicConfig(level=logging.INFO) | ||
repository = os.getenv("GITHUB_REPOSITORY") | ||
ref_name = os.getenv("GITHUB_REF_NAME") | ||
image_tag = os.getenv("image_tag", "") | ||
try: | ||
logging.info(f"Fetching existing release notes, if any, for repository = {repository}, tag = {ref_name} ...") | ||
request = urllib.request.urlopen(f"https://api.github.com/repos/{repository}/releases/tags/{ref_name}") | ||
data = json.loads(request.read()) | ||
existing_notes = data["body"] | ||
except Exception as err: | ||
logging.info("An error ocurred while fetching existing release notes (perhaps there is no release yet).") | ||
existing_notes = "" | ||
notes = existing_notes + "\n" | ||
notes += "Built using Docker image: **multiversx/sdk-rust-contract-builder:${{ inputs.image_tag }}**.\n" | ||
notes += "\n" | ||
notes+= "## Codehashes (blake2b):\n" | ||
for wasm_file in Path("/home/runner/work/output-from-docker").rglob("*.wasm"): | ||
code = wasm_file.read_bytes() | ||
h = blake2b(digest_size=32) | ||
h.update(code) | ||
notes += f"**{wasm_file.name}**: `{h.hexdigest()}`" | ||
Path("release_notes.txt").write_text(notes) | ||
EOF | ||
env: | ||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||
image_tag: ${{ inputs.image_tag }} | ||
|
||
- name: Upload artifacts to release, edit release notes | ||
if: ${{ inputs.attach_to_existing_release == true || inputs.create_release == true }} | ||
run: | | ||
gh release edit ${{ github.ref_name }} --notes-file=release_notes.txt | ||
gh release upload ${{ github.ref_name }} $(find /home/runner/work/output-from-docker/**/*.wasm -type f) | ||
gh release upload ${{ github.ref_name }} $(find /home/runner/work/output-from-docker/**/*.source.json -type f) | ||
gh release upload ${{ github.ref_name }} $(find /home/runner/work/output-from-docker/artifacts.json -type f) | ||
env: | ||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} |
Oops, something went wrong.