diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f436c5ba..04d4e672 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,9 +2,9 @@ name: CI on: pull_request: - branches: [dev] + branches: [dev, main] push: - branches: [dev] + branches: [dev, main] workflow_dispatch: permissions: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 9c0498b2..3a4aaf64 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -312,17 +312,17 @@ jobs: password: ${{ secrets.DOCKER_PASSWORD }} repository: archivebox/archivebox - downstream-packages: - name: Verify downstream packages + cascade: + name: Cascade verified stable release if: github.event.workflow_run.head_branch == 'main' && !contains(needs.python-release.outputs.tag, 'rc') needs: - python-release - docker-release permissions: - actions: read contents: read - uses: ./.github/workflows/update-homebrew-tap.yml + uses: ArchiveBox/monorepo/.github/workflows/cascade-release.yml@main with: - release_tag: ${{ needs.python-release.outputs.tag }} - release_sha: ${{ github.event.workflow_run.head_sha }} - secrets: inherit + upstream_package: archivebox + upstream_version: ${{ needs.python-release.outputs.version }} + secrets: + release_token: ${{ secrets.RELEASE_GH_TOKEN }} diff --git a/.github/workflows/update-homebrew-tap.yml b/.github/workflows/update-homebrew-tap.yml deleted file mode 100644 index ae100500..00000000 --- a/.github/workflows/update-homebrew-tap.yml +++ /dev/null @@ -1,172 +0,0 @@ -name: Update downstream packages - -on: - workflow_call: - inputs: - release_tag: - required: true - type: string - release_sha: - required: true - type: string - workflow_dispatch: - inputs: - release_tag: - description: Exact ArchiveBox release tag - required: true - type: string - release_sha: - description: Full commit SHA owned by the release tag - required: true - type: string - -permissions: - actions: read - contents: read - -concurrency: - group: update-downstream-packages-${{ inputs.release_tag || github.ref_name }} - cancel-in-progress: false - -jobs: - dispatch: - runs-on: ubuntu-24.04 - steps: - - name: Checkout release source - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 - with: - ref: ${{ inputs.release_sha || github.sha }} - fetch-depth: 1 - - - name: Set up Python - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5 - with: - python-version: "3.13" - - - name: Install uv - uses: astral-sh/setup-uv@d0cc045d04ccac9d8b7881df0226f9e82c39688e # v6 - with: - version: "0.11.3" - - - name: Resolve release binaries through abxpkg - env: - ABXPKG_LIB_DIR: ${{ runner.temp }}/abx-lib - shell: bash - run: | - set -Eeuo pipefail - ABXPKG_VERSION="$(uv run --no-cache --no-project python - <<'PY' - import tomllib - from pathlib import Path - - packages = tomllib.loads(Path("uv.lock").read_text())["package"] - matches = [package["version"] for package in packages if package["name"] == "abxpkg"] - if len(matches) != 1: - raise SystemExit(f"Expected one locked abxpkg package, found {len(matches)}") - print(matches[0]) - PY - )" - test -n "$ABXPKG_VERSION" - mkdir -p "$ABXPKG_LIB_DIR/env/bin" - echo "ABXPKG_LIB_DIR=$ABXPKG_LIB_DIR" >> "$GITHUB_ENV" - - uv run --no-cache --no-project --with "abxpkg==$ABXPKG_VERSION" abxpkg env \ - --install \ - --lib="$ABXPKG_LIB_DIR" \ - --deps-from="$GITHUB_WORKSPACE/.github/configs/ci-tooling.json:release_binaries" \ - >/dev/null - - for binary_name in uv git jq curl; do - binary="$ABXPKG_LIB_DIR/env/bin/$binary_name" - test -L "$binary" - test -x "$binary" - done - { - echo "UV_BINARY=$ABXPKG_LIB_DIR/env/bin/uv" - echo "GIT_BINARY=$ABXPKG_LIB_DIR/env/bin/git" - echo "JQ_BINARY=$ABXPKG_LIB_DIR/env/bin/jq" - echo "CURL_BINARY=$ABXPKG_LIB_DIR/env/bin/curl" - } >> "$GITHUB_ENV" - - - name: Verify the exact release and downstream builds - env: - DOWNSTREAM_TOKEN: ${{ secrets.RELEASE_GH_TOKEN || secrets.HOMEBREW_TAP_TOKEN }} - INPUT_RELEASE_TAG: ${{ inputs.release_tag }} - INPUT_RELEASE_SHA: ${{ inputs.release_sha }} - shell: bash - run: | - set -Eeuo pipefail - [[ -n "$DOWNSTREAM_TOKEN" ]] || { echo "RELEASE_GH_TOKEN/HOMEBREW_TAP_TOKEN is not configured" >&2; exit 1; } - - RELEASE_TAG="${INPUT_RELEASE_TAG:-${GITHUB_REF_NAME}}" - [[ "$RELEASE_TAG" == v* ]] - VERSION="${RELEASE_TAG#v}" - - TAG_TARGET="$("$GIT_BINARY" ls-remote "https://github.com/${GITHUB_REPOSITORY}.git" "refs/tags/${RELEASE_TAG}^{}" | "$UV_BINARY" run --no-cache --no-project python -c 'import sys; print(next((line.split()[0] for line in sys.stdin if line.strip()), ""))')" - if [[ -z "$TAG_TARGET" ]]; then - TAG_TARGET="$("$GIT_BINARY" ls-remote "https://github.com/${GITHUB_REPOSITORY}.git" "refs/tags/${RELEASE_TAG}" | "$UV_BINARY" run --no-cache --no-project python -c 'import sys; print(next((line.split()[0] for line in sys.stdin if line.strip()), ""))')" - fi - RELEASE_SHA="${INPUT_RELEASE_SHA:-${TAG_TARGET}}" - [[ "$RELEASE_SHA" =~ ^[0-9a-f]{40}$ ]] - [[ "$TAG_TARGET" == "$RELEASE_SHA" ]] || { echo "${RELEASE_TAG} points to ${TAG_TARGET}, not ${RELEASE_SHA}" >&2; exit 1; } - - PAYLOAD_FILTER="{event_type: \"archivebox-dev-updated\", client_payload: {ref: \$ref, sha: \$sha, version: \$version}}" - PAYLOAD="$("$JQ_BINARY" -nc --arg ref "$RELEASE_TAG" --arg sha "$RELEASE_SHA" --arg version "$VERSION" "$PAYLOAD_FILTER")" - - dispatch_and_wait() { - local repo="$1" - local workflow_name="$2" - local dispatched_at run_json status conclusion url - - dispatched_at="$(date -u +"%Y-%m-%dT%H:%M:%SZ")" - "$CURL_BINARY" --fail-with-body \ - -X POST \ - -H "Accept: application/vnd.github+json" \ - -H "Authorization: Bearer ${DOWNSTREAM_TOKEN}" \ - -H "X-GitHub-Api-Version: 2022-11-28" \ - "https://api.github.com/repos/ArchiveBox/${repo}/dispatches" \ - --data "$PAYLOAD" - - local deadline=$((SECONDS + 1800)) - while (( SECONDS < deadline )); do - run_json="$("$CURL_BINARY" --fail-with-body \ - -H "Accept: application/vnd.github+json" \ - -H "Authorization: Bearer ${DOWNSTREAM_TOKEN}" \ - -H "X-GitHub-Api-Version: 2022-11-28" \ - "https://api.github.com/repos/ArchiveBox/${repo}/actions/runs?event=repository_dispatch&branch=main&per_page=20" \ - | "$JQ_BINARY" -c \ - --arg workflow_name "$workflow_name" \ - --arg dispatched_at "$dispatched_at" \ - '.workflow_runs - | map(select(.name == $workflow_name and .created_at >= $dispatched_at)) - | sort_by(.created_at) - | reverse - | .[0] // empty')" - - if [[ -n "$run_json" ]]; then - status="$("$JQ_BINARY" -r '.status' <<< "$run_json")" - conclusion="$("$JQ_BINARY" -r '.conclusion // ""' <<< "$run_json")" - url="$("$JQ_BINARY" -r '.html_url' <<< "$run_json")" - echo "${repo}: ${status:-unknown} ${conclusion:-pending} ${url}" - if [[ "$status" == "completed" ]]; then - [[ "$conclusion" == "success" ]] || { - echo "${repo} downstream workflow failed: ${url}" >&2 - return 1 - } - return 0 - fi - else - echo "${repo}: waiting for ${workflow_name} repository_dispatch run" - fi - sleep 30 - done - - echo "${repo} downstream workflow did not complete within 30 minutes" >&2 - return 1 - } - - for repo_and_workflow in \ - "homebrew-archivebox|Update ArchiveBox dev formula" \ - "debian-archivebox|Build Debian package" - do - dispatch_and_wait "${repo_and_workflow%%|*}" "${repo_and_workflow#*|}" - done