Publish crates #2
This file contains hidden or 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
| name: Publish crates | |
| on: | |
| push: | |
| tags: | |
| - 'v*' | |
| - '[0-9]*' | |
| workflow_dispatch: | |
| inputs: | |
| version: | |
| description: 'Version to publish. Defaults to the pushed tag name without leading v.' | |
| required: false | |
| type: string | |
| pd_vm_version: | |
| description: 'pd-vm dependency version override' | |
| required: false | |
| type: string | |
| pd_edge_abi_version: | |
| description: 'pd-edge-abi dependency version override' | |
| required: false | |
| type: string | |
| concurrency: | |
| group: publish-crates-${{ github.ref }} | |
| cancel-in-progress: false | |
| jobs: | |
| publish: | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| env: | |
| PACKAGE_ORDER: pd-vm-compat-frontends | |
| PD_VM_VERSION: ${{ inputs.pd_vm_version || '0.22.2' }} | |
| PD_EDGE_ABI_VERSION: ${{ inputs.pd_edge_abi_version || '0.1.1' }} | |
| WORKSPACE_VERSION_OVERRIDES: rustscript-compat-frontends=${{ inputs.version }} rustscript=${{ inputs.pd_vm_version || '0.22.2' }} pd-edge=${{ inputs.pd_edge_abi_version || '0.1.1' }} | |
| steps: | |
| - name: Checkout compatibility frontends | |
| uses: actions/checkout@v4 | |
| with: | |
| path: rustscript-compat-frontends | |
| - name: Checkout rustscript dependency | |
| uses: actions/checkout@v4 | |
| with: | |
| repository: rustscript-lang/rustscript | |
| path: rustscript | |
| - name: Checkout pd-edge dependency | |
| uses: actions/checkout@v4 | |
| with: | |
| repository: rustscript-lang/pd-edge | |
| path: pd-edge | |
| - name: Setup Rust | |
| uses: dtolnay/rust-toolchain@stable | |
| - name: Publish crate | |
| working-directory: rustscript-compat-frontends | |
| env: | |
| CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} | |
| INPUT_VERSION: ${{ inputs.version }} | |
| run: | | |
| set -euo pipefail | |
| if [[ -z "${CARGO_REGISTRY_TOKEN:-}" ]]; then | |
| echo "CARGO_REGISTRY_TOKEN is required" >&2 | |
| exit 1 | |
| fi | |
| version="${INPUT_VERSION:-}" | |
| if [[ -z "$version" ]]; then | |
| version="${GITHUB_REF_NAME#v}" | |
| fi | |
| if [[ -z "$version" ]]; then | |
| echo "version is required" >&2 | |
| exit 1 | |
| fi | |
| export PUBLISH_VERSION="$version" | |
| python3 - <<'PY' | |
| import os, re | |
| from pathlib import Path | |
| version = os.environ['PUBLISH_VERSION'] | |
| dep_versions = { | |
| 'pd-vm': os.environ.get('PD_VM_VERSION') or '0.22.2', | |
| 'pd-host-function': os.environ.get('PD_VM_VERSION') or '0.22.2', | |
| 'pd-edge-abi': os.environ.get('PD_EDGE_ABI_VERSION') or '0.1.1', | |
| } | |
| workspace_versions = {} | |
| for item in os.environ.get('WORKSPACE_VERSION_OVERRIDES', '').split(): | |
| if '=' in item: | |
| name, value = item.split('=', 1) | |
| if value: | |
| workspace_versions[name] = value | |
| dep_re = re.compile(r'^(\s*([A-Za-z0-9_-]+)\s*=\s*\{)(.*)(\}\s*)$') | |
| for path in Path('..').rglob('Cargo.toml'): | |
| if '.git' in path.parts or 'target' in path.parts: | |
| continue | |
| manifest_version = workspace_versions.get(path.parent.name, version) | |
| lines = path.read_text().splitlines() | |
| out = [] | |
| section = '' | |
| for line in lines: | |
| stripped = line.strip() | |
| if stripped.startswith('[') and stripped.endswith(']'): | |
| section = stripped | |
| if section in ('[workspace.package]', '[package]') and stripped.startswith('version = '): | |
| line = re.sub(r'version\s*=\s*"[^"]*"', f'version = "{manifest_version}"', line) | |
| match = dep_re.match(line) | |
| if match and 'path' in match.group(3): | |
| prefix, key, body, suffix = match.groups() | |
| package_match = re.search(r'package\s*=\s*"([^"]+)"', body) | |
| dep_name = package_match.group(1) if package_match else key | |
| dep_version = dep_versions.get(dep_name) | |
| if dep_version: | |
| if re.search(r'version\s*=\s*"[^"]*"', body): | |
| body = re.sub(r'version\s*=\s*"[^"]*"', f'version = "{dep_version}"', body) | |
| else: | |
| body = body.rstrip() | |
| if body and not body.endswith(','): | |
| body += ',' | |
| body += f' version = "{dep_version}"' | |
| line = prefix + body + suffix | |
| out.append(line) | |
| path.write_text('\n'.join(out) + '\n') | |
| PY | |
| python3 - <<'PY' | |
| import os, subprocess, sys, time, urllib.error, urllib.request | |
| version = os.environ['PUBLISH_VERSION'] | |
| packages = os.environ['PACKAGE_ORDER'].split() | |
| token = os.environ['CARGO_REGISTRY_TOKEN'] | |
| def exists(crate, crate_version): | |
| url = f'https://crates.io/api/v1/crates/{crate}/{crate_version}' | |
| try: | |
| urllib.request.urlopen(url, timeout=20).read() | |
| return True | |
| except urllib.error.HTTPError as exc: | |
| if exc.code == 404: | |
| return False | |
| raise | |
| for package in packages: | |
| if exists(package, version): | |
| print(f'{package} {version} already published; skipping') | |
| continue | |
| for attempt in range(1, 19): | |
| print(f'publishing {package} {version}, attempt {attempt}') | |
| proc = subprocess.run([ | |
| 'cargo', 'publish', '-p', package, '--no-verify', '--allow-dirty', '--token', token, | |
| ], text=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) | |
| print(proc.stdout) | |
| if proc.returncode == 0 or exists(package, version): | |
| break | |
| if attempt == 18: | |
| sys.exit(proc.returncode) | |
| time.sleep(20) | |
| PY |