name: Update & Push Binary to SourceForge on: workflow_dispatch: inputs: target_os: description: "Target OS" required: true type: choice options: - windows - linux - macos - android target_arch: description: "Architecture" required: true type: choice options: - x86_64 - arm64 - arm64-v8a tool_name: description: "Tool Key in manifest.json (e.g. maxcso, ecm)" required: true type: string binary_filename: description: "Binary Filename (e.g. maxcso.exe or maxcso)" required: true type: string download_url: description: "Direct Download URL (must be https on allowed domain)" required: true type: string version: description: "Tool version string" required: true type: string expected_sha256: description: "Optional expected SHA-256 for verification" required: false type: string dry_run: description: "Dry run mode" type: boolean required: false default: true permissions: contents: write pull-requests: write jobs: download-and-publish: name: Download & Publish ${{ inputs.tool_name }} (${{ inputs.target_os }}/${{ inputs.target_arch }}) runs-on: ubuntu-22.04 steps: - name: Checkout OpenROM-bins uses: actions/checkout@v4 - name: Validate Inputs & Domain Allowlist env: TOOL: ${{ inputs.tool_name }} FILENAME: ${{ inputs.binary_filename }} URL: ${{ inputs.download_url }} EXPECTED_SHA: ${{ inputs.expected_sha256 }} run: | python3 -c ' import os, sys, re from urllib.parse import urlparse tool = os.environ.get("TOOL", "").lower() filename = os.environ.get("FILENAME", "") url = os.environ.get("URL", "") expected_sha = os.environ.get("EXPECTED_SHA", "") if tool == "nkit": sys.exit("Error: NKit is maintained manually (no build workflow). See README.") if not re.match(r"^[a-z0-9-]+$", tool): sys.exit(f"Invalid tool_name format: {tool}") if not re.match(r"^[A-Za-z0-9._-]+$", filename): sys.exit(f"Invalid binary_filename format: {filename}") if expected_sha and not re.match(r"^[0-9a-fA-F]{64}$", expected_sha): sys.exit(f"Invalid expected_sha256 format: {expected_sha}") parsed = urlparse(url) if parsed.scheme != "https": sys.exit("Error: download_url must use https scheme!") allowed_domains = ["github.com", "objects.githubusercontent.com", "registry.npmjs.org"] if parsed.netloc.lower() not in allowed_domains: sys.exit(f"Error: domain {parsed.netloc} not in allowlist {allowed_domains}") print("Inputs and domain allowlist validated successfully.") ' - name: Download & Verify Binary env: URL: ${{ inputs.download_url }} FILENAME: ${{ inputs.binary_filename }} EXPECTED_SHA: ${{ inputs.expected_sha256 }} run: | mkdir -p artifact_dir curl --retry 5 -fL "$URL" -o "artifact_dir/$FILENAME" if [ -n "$EXPECTED_SHA" ]; then ACTUAL_SHA=$(sha256sum "artifact_dir/$FILENAME" | awk '{print $1}') if [ "$ACTUAL_SHA" != "$EXPECTED_SHA" ]; then echo "Error: SHA-256 mismatch!" >&2 echo " Expected: $EXPECTED_SHA" >&2 echo " Actual: $ACTUAL_SHA" >&2 exit 1 fi echo "Expected SHA-256 verified successfully." fi - name: Upload Artifact uses: actions/upload-artifact@v4 with: name: binary-${{ inputs.tool_name }}-${{ inputs.target_os }}-${{ inputs.target_arch }} path: artifact_dir/${{ inputs.binary_filename }} publish: needs: download-and-publish uses: ./.github/workflows/publish-sourceforge.yml with: os: ${{ inputs.target_os }} arch: ${{ inputs.target_arch }} tool: ${{ inputs.tool_name }} artifact_name: binary-${{ inputs.tool_name }}-${{ inputs.target_os }}-${{ inputs.target_arch }} version: ${{ inputs.version }} build_flavor: upstream-download dry_run: ${{ inputs.dry_run }}