Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
182 changes: 182 additions & 0 deletions .github/workflows/release-op-conductor-ops.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
name: release op-conductor-ops

on:
push:
tags:
- 'op-conductor-ops/v*'
pull_request:
branches: [main]
paths:
- 'op-conductor-ops/**'
- '.github/workflows/release-op-conductor-ops.yaml'
workflow_dispatch:

permissions:
contents: read

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}

jobs:
build:
strategy:
fail-fast: false
matrix:
include:
- platform: darwin-arm64
runner: macos-14
- platform: darwin-amd64
runner: macos-15-intel
- platform: linux-amd64
runner: ubuntu-22.04
- platform: linux-arm64
runner: ubuntu-22.04-arm
runs-on: ${{ matrix.runner }}
permissions:
contents: read
id-token: write
attestations: write
steps:
- name: Checkout
uses: actions/checkout@71cf2267d89c5cb81562390fa70a37fa40b1305e # v6
with:
persist-credentials: false

- name: Set up Python
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7
with:
python-version: '3.12'

- name: Resolve version
id: version
run: |
set -euo pipefail
if [ "${GITHUB_REF_TYPE}" = "tag" ]; then
version="${GITHUB_REF_NAME#op-conductor-ops/v}"
else
version="0.0.0-dev"
fi
echo "version=${version}" >> "$GITHUB_OUTPUT"
echo "Building version ${version}"

- name: Install dependencies
working-directory: op-conductor-ops
run: |
set -euo pipefail
pip install "poetry==2.3.4"
poetry check --lock
poetry install --no-interaction --with dev

- name: Build executable
working-directory: op-conductor-ops
env:
ASSET: op-conductor-ops-${{ steps.version.outputs.version }}-${{ matrix.platform }}
run: |
set -euo pipefail
poetry run pyinstaller --onefile --clean --noconfirm \
--name "${ASSET}" entrypoint.py

- name: Smoke test executable
working-directory: op-conductor-ops
env:
ASSET: op-conductor-ops-${{ steps.version.outputs.version }}-${{ matrix.platform }}
run: |
set -euo pipefail
file "dist/${ASSET}"
"./dist/${ASSET}" --help

# Only released binaries get an attestation; PR and dispatch builds are throwaway.
- name: Attest build provenance
if: github.ref_type == 'tag'
uses: actions/attest-build-provenance@0f67c3f4856b2e3261c31976d6725780e5e4c373 # v4
with:
subject-path: op-conductor-ops/dist/op-conductor-ops-${{ steps.version.outputs.version }}-${{ matrix.platform }}

- name: Compute checksum
working-directory: op-conductor-ops/dist
env:
ASSET: op-conductor-ops-${{ steps.version.outputs.version }}-${{ matrix.platform }}
run: |
set -euo pipefail
shasum -a 256 "${ASSET}" > "${ASSET}.sha256"
cat "${ASSET}.sha256"

- name: Upload artifacts
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
with:
name: op-conductor-ops-${{ matrix.platform }}
path: |
op-conductor-ops/dist/op-conductor-ops-*-${{ matrix.platform }}
op-conductor-ops/dist/op-conductor-ops-*-${{ matrix.platform }}.sha256
if-no-files-found: error

release:
needs: [build]
if: github.ref_type == 'tag'
runs-on: ubuntu-24.04
permissions:
contents: write
attestations: read
steps:
- name: Resolve version
id: version
run: |
set -euo pipefail
version="${GITHUB_REF_NAME#op-conductor-ops/v}"
echo "version=${version}" >> "$GITHUB_OUTPUT"

- name: Download artifacts
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
with:
path: dist
merge-multiple: true

- name: Assemble checksums file
working-directory: dist
env:
VERSION: ${{ steps.version.outputs.version }}
run: |
set -euo pipefail
cat ./*.sha256 | sort -k2 > "op-conductor-ops_${VERSION}_checksums.txt"
rm -f ./*.sha256
cat "op-conductor-ops_${VERSION}_checksums.txt"
test "$(grep -c . "op-conductor-ops_${VERSION}_checksums.txt")" -eq 4

# The checksums file is generated here and is deliberately not attested.
- name: Verify build provenance
env:
GH_TOKEN: ${{ github.token }}
run: |
set -euo pipefail
for f in dist/op-conductor-ops-*-*; do
gh attestation verify "$f" --repo "${GITHUB_REPOSITORY}"
done

- name: Publish release
env:
GH_TOKEN: ${{ github.token }}
VERSION: ${{ steps.version.outputs.version }}
run: |
set -euo pipefail
# Reuse an existing draft so a rerun repairs a partial upload instead of
# failing on create, then publish only after all uploads succeed. Refuse to
# touch an already-published release: PyInstaller output is not reproducible,
# so clobbering assets would invalidate the SHA-256 digests op-toolbox pins.
draft="$(gh release view "${GITHUB_REF_NAME}" --repo "${GITHUB_REPOSITORY}" \
--json isDraft --jq .isDraft 2>/dev/null || echo missing)"
case "${draft}" in
false)
echo "::error::${GITHUB_REF_NAME} is already published; delete the release or cut a new version" >&2
exit 1
;;
missing)
gh release create "${GITHUB_REF_NAME}" \
--repo "${GITHUB_REPOSITORY}" \
--draft \
--title "op-conductor-ops ${VERSION}" \
--generate-notes
;;
esac
gh release upload "${GITHUB_REF_NAME}" --repo "${GITHUB_REPOSITORY}" dist/* --clobber
gh release edit "${GITHUB_REF_NAME}" --repo "${GITHUB_REPOSITORY}" --draft=false
3 changes: 3 additions & 0 deletions op-conductor-ops/.gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,4 @@
__pycache__/
build/
dist/
*.spec
2 changes: 1 addition & 1 deletion op-conductor-ops/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,6 @@ COPY /op-conductor-ops/pyproject.toml /op-conductor-ops/poetry.lock* /op-conduct
COPY /op-conductor-ops/op_conductor_ops/ ./op_conductor_ops/

RUN python3.12 -m poetry config virtualenvs.create false \
&& python3.12 -m poetry install --no-interaction --no-ansi
&& python3.12 -m poetry install --no-interaction --no-ansi --only main

ENTRYPOINT ["op-conductor-ops"]
59 changes: 46 additions & 13 deletions op-conductor-ops/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,29 +4,62 @@ op-conductor-ops is a CLI tool for managing op-conductor sequencer clusters.

**WARNING!!! This tool can cause a network outage if used improperly. Please consult #pod-devinfra before using.**

## Setup
## Install

### Via op-toolbox (recommended for operators)

```sh
op-toolbox install op-conductor-ops
op-toolbox op-conductor-ops status <network-name>
```

### Direct binary download

Releases attach a self-contained executable per platform; no Python is required.

```sh
version=0.2.0
platform=darwin-arm64 # or darwin-amd64, linux-amd64, linux-arm64
gh release download "op-conductor-ops/v${version}" \
--repo ethereum-optimism/infra \
--pattern "op-conductor-ops-${version}-${platform}"
chmod +x "op-conductor-ops-${version}-${platform}"
./op-conductor-ops-${version}-${platform} --help
```

Verify the download against `op-conductor-ops_${version}_checksums.txt` from the
same release.

### From source (development)

Requires [poetry](https://github.com/python-poetry/poetry).

Install the package and its dependencies with `poetry install`. This registers the
`op-conductor-ops` console script (see `[tool.poetry.scripts]` in `pyproject.toml`).
```sh
poetry install
poetry run op-conductor-ops --help
```

Recommended update to your .bashrc/zshrc:
Build the executable locally with `just build-binary 0.2.0 darwin-arm64`.

1. `export CONDUCTOR_CONFIG="<path-to-op-conductor-ops-config.toml>"`
## Configuration

## Usage
Recommended addition to your `.bashrc`/`.zshrc`:

After installing with `poetry install`, the tool is invoked with `poetry run op-conductor-ops`
(or just `op-conductor-ops` from within `poetry shell`), passing on any arguments.
```sh
export CONDUCTOR_CONFIG="<path-to-op-conductor-ops-config.toml>"
```

## Usage

### Example Usage
```sh
# Implicit config lookup at ./config.toml or $CONDUCTOR_CONFIG
op-conductor-ops status <network-name>

* Example usage with implicit config file with lookup at ./config.toml
```poetry run op-conductor-ops status <network-name>```
# Explicit config and certificate paths
op-conductor-ops -c ./<path>/config.toml --cert ./<path>/cacert.pem <command> <network-name>
```

* Usage with explicit path to config and certificate
```poetry run op-conductor-ops -c ./<path>/config.toml --cert ./<path>/cacert.pem <command> <network-name>```
From a source checkout, prefix the above with `poetry run`.

## Example Configuration File: example.config.toml

Expand Down
3 changes: 3 additions & 0 deletions op-conductor-ops/entrypoint.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from op_conductor_ops.cli import app

app()
6 changes: 6 additions & 0 deletions op-conductor-ops/justfile
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
lint:
poetry run black .

# Build a single-file executable. VERSION has no leading "v" (e.g. 0.2.0).
# PLATFORM is one of darwin-arm64, darwin-amd64, linux-amd64, linux-arm64.
build-binary VERSION PLATFORM:
poetry run pyinstaller --onefile --clean --noconfirm \
--name "op-conductor-ops-{{VERSION}}-{{PLATFORM}}" \
entrypoint.py
Loading
Loading