From 38cc4327e074ddab605ddbaaf60f1dbbce5f2c25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sergio=20G=C3=B3mez?= Date: Tue, 20 Oct 2026 11:20:50 +0200 Subject: [PATCH 1/3] =?UTF-8?q?ci(macos):=20instala=20y=20desinstala=20el?= =?UTF-8?q?=20cask=20con=20el=20paquete=20reci=C3=A9n=20construido?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implements #2060: El CI de macOS instala y desinstala el cask con el paquete recién construido - tap local de usar y tirar con el cask del tap reescrito a file://, sin escribir en el tap real --- .github/workflows/build.yml | 3 ++ justfile | 1 + packaging/macos/verify-cask.sh | 41 ++++++++++++++++++++ packaging/macos/verify-cask.test.sh | 59 +++++++++++++++++++++++++++++ 4 files changed, 104 insertions(+) create mode 100755 packaging/macos/verify-cask.sh create mode 100755 packaging/macos/verify-cask.test.sh diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b602895c..fe533543 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -265,6 +265,9 @@ jobs: - name: Empaqueta la .app y el .dmg run: just bundle + - name: Instala y desinstala el cask con ese .dmg + run: packaging/macos/verify-cask.sh rfirma-app/src-tauri/target/aarch64-apple-darwin/release/bundle/dmg/*.dmg + - name: Sube el .dmg y la actualizacion uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: diff --git a/justfile b/justfile index 7283d2c0..d8f07607 100644 --- a/justfile +++ b/justfile @@ -126,6 +126,7 @@ check-repo: check-version fmt-check {{ root }}/packaging/repo/publish-tree.test.sh {{ root }}/packaging/windows/sign-updater.test.sh {{ root }}/packaging/verify-packages.test.sh + {{ root }}/packaging/macos/verify-cask.test.sh {{ root }}/packaging/check_launchers.py python3 -m unittest discover -s {{ root }}/packaging -p 'test_check_launchers.py' python3 -m unittest discover -s {{ root }}/scripts/tests -p 'test_*.py' diff --git a/packaging/macos/verify-cask.sh b/packaging/macos/verify-cask.sh new file mode 100755 index 00000000..97d634bb --- /dev/null +++ b/packaging/macos/verify-cask.sh @@ -0,0 +1,41 @@ +#!/usr/bin/env bash +# Instala el cask del tap con el .dmg recién construido, arranca la app y la desinstala, sin escribir en el tap (TD-139). +set -euo pipefail + +dmg="${1:?uso: verify-cask.sh }" +dmg="$(cd "$(dirname "$dmg")" && pwd)/$(basename "$dmg")" +source="${CASK_SOURCE:-https://raw.githubusercontent.com/sgomez/homebrew-rfirma/main/Casks/rfirma.rb}" +applications="${APPLICATIONS_DIR:-/Applications}" +tap="rfirma-ci/local" + +tmp="$(mktemp -d)" +cleanup() { + brew untap "$tap" > /dev/null 2>&1 || true + rm -rf "$tmp" +} +trap cleanup EXIT + +case "$source" in + http*) curl -fsSL "$source" -o "$tmp/rfirma.rb" ;; + *) cp "$source" "$tmp/rfirma.rb" ;; +esac + +sha="$(shasum -a 256 "$dmg" | cut -d' ' -f1)" +sed -E \ + -e "s|^( *)version .*|\\1version \"0.0.0\"|" \ + -e "s|^( *)sha256 .*|\\1sha256 \"$sha\"|" \ + -e "s|^( *)url \"[^\"]*\".*|\\1url \"file://$dmg\"|" \ + "$tmp/rfirma.rb" > "$tmp/cask.rb" + +brew tap-new --no-git "$tap" > /dev/null +mkdir -p "$(brew --repository "$tap")/Casks" +cp "$tmp/cask.rb" "$(brew --repository "$tap")/Casks/rfirma.rb" + +brew install --cask "$tap/rfirma" +app="$applications/rfirma.app" +[ -d "$app" ] || { echo "$app no existe tras instalar" >&2; exit 1; } +"$app/Contents/MacOS/rfirma" --version + +brew uninstall --cask "$tap/rfirma" +[ ! -e "$app" ] || { echo "$app sigue ahí tras desinstalar" >&2; exit 1; } +echo "cask: instala, arranca y desinstala" diff --git a/packaging/macos/verify-cask.test.sh b/packaging/macos/verify-cask.test.sh new file mode 100755 index 00000000..4fddc04e --- /dev/null +++ b/packaging/macos/verify-cask.test.sh @@ -0,0 +1,59 @@ +#!/usr/bin/env bash +# Las pruebas de `verify-cask.sh` con un `brew` de mentira, sin red. +set -euo pipefail + +root="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" +verify="$root/packaging/macos/verify-cask.sh" +tmp="$(mktemp -d)" +trap 'rm -rf "$tmp"' EXIT + +mkdir -p "$tmp/bin" "$tmp/apps" "$tmp/repo" +printf 'dmg de prueba' > "$tmp/rfirma_1.0.0_aarch64.dmg" +expected_sha="$(shasum -a 256 "$tmp/rfirma_1.0.0_aarch64.dmg" | cut -d' ' -f1)" + +cat > "$tmp/cask.rb" <<'CASK' +cask "rfirma" do + version "0.0.0" + sha256 "0000000000000000000000000000000000000000000000000000000000000000" + + url "https://github.com/sgomez/rfirma/releases/download/v#{version}/rfirma_#{version}_aarch64.dmg" + app "rfirma.app" +end +CASK + +cat > "$tmp/bin/brew" <<'BREW' +#!/usr/bin/env bash +echo "$*" >> "$FAKE_BREW_LOG" +case "$1" in + tap-new) mkdir -p "$FAKE_REPO/Casks" ;; + --repository) echo "$FAKE_REPO" ;; + install) + cp "$FAKE_REPO/Casks/rfirma.rb" "$FAKE_INSTALLED_CASK" + mkdir -p "$APPLICATIONS_DIR/rfirma.app/Contents/MacOS" + printf '#!/bin/sh\necho "rfirma 1.0.0"\n' > "$APPLICATIONS_DIR/rfirma.app/Contents/MacOS/rfirma" + chmod +x "$APPLICATIONS_DIR/rfirma.app/Contents/MacOS/rfirma" + ;; + uninstall) [ -n "${FAKE_KEEP_APP:-}" ] || rm -rf "$APPLICATIONS_DIR/rfirma.app" ;; +esac +BREW +chmod +x "$tmp/bin/brew" + +export PATH="$tmp/bin:$PATH" FAKE_REPO="$tmp/repo" FAKE_BREW_LOG="$tmp/brew.log" +export FAKE_INSTALLED_CASK="$tmp/installed.rb" APPLICATIONS_DIR="$tmp/apps" CASK_SOURCE="$tmp/cask.rb" + +fail() { echo "FALLO $*" >&2; exit 1; } + +"$verify" "$tmp/rfirma_1.0.0_aarch64.dmg" > "$tmp/out" 2>&1 || fail "debía pasar: $(cat "$tmp/out")" +grep -q '^tap-new --no-git' "$tmp/brew.log" || fail "el tap de usar y tirar debe crearse sin git" +grep -qF "url \"file://$tmp/rfirma_1.0.0_aarch64.dmg\"" "$tmp/installed.rb" || fail "el cask debe apuntar al dmg local" +grep -q "sha256 \"$expected_sha\"" "$tmp/installed.rb" || fail "el cask debe llevar el sha256 del dmg local" +grep -q 'app "rfirma.app"' "$tmp/installed.rb" || fail "el resto del cask debe quedar igual" +grep -q '^uninstall --cask' "$tmp/brew.log" || fail "debe desinstalar" +[ ! -e "$tmp/apps/rfirma.app" ] || fail "la app debe haberse retirado" + +if FAKE_KEEP_APP=1 "$verify" "$tmp/rfirma_1.0.0_aarch64.dmg" > "$tmp/out" 2>&1; then + fail "si la desinstalación deja la app, tenía que fallar" +fi +grep -q 'sigue' "$tmp/out" || fail "esperaba el aviso de que la app sigue: $(cat "$tmp/out")" + +echo "OK verify-cask" From 9e16915cff4e3bf6b113bb1bbeac6c9cb1a3dc47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sergio=20G=C3=B3mez?= Date: Tue, 20 Oct 2026 11:25:54 +0200 Subject: [PATCH 2/3] fix(pr): address review comments on #2062 --- .github/workflows/build.yml | 25 +++++++++++++++++++++---- packaging/macos/verify-cask.sh | 1 + packaging/macos/verify-cask.test.sh | 7 +++++-- 3 files changed, 27 insertions(+), 6 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index fe533543..663818ce 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -265,9 +265,6 @@ jobs: - name: Empaqueta la .app y el .dmg run: just bundle - - name: Instala y desinstala el cask con ese .dmg - run: packaging/macos/verify-cask.sh rfirma-app/src-tauri/target/aarch64-apple-darwin/release/bundle/dmg/*.dmg - - name: Sube el .dmg y la actualizacion uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: @@ -278,9 +275,28 @@ jobs: if-no-files-found: error retention-days: 1 + # El cask pide macOS 15 (Sequoia) y la app 15.4 (ADR-0040): no se prueba en el runner de compilacion. + verify-cask: + name: Cask de macOS + needs: macos + runs-on: macos-15 + timeout-minutes: 15 + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + ref: ${{ inputs.ref }} + + - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + name: bundle-macos + path: dmg + + - name: Instala y desinstala el cask con ese .dmg + run: packaging/macos/verify-cask.sh dmg/*.dmg + collect-packages: name: Reune y verifica paquetes - needs: [native-bridge, test-native, flatpak, bundle, windows, macos] + needs: [native-bridge, test-native, flatpak, bundle, windows, macos, verify-cask] if: | always() && !cancelled() && @@ -289,6 +305,7 @@ jobs: needs.flatpak.result == 'success' && needs.windows.result == 'success' && needs.macos.result == 'success' && + needs.verify-cask.result == 'success' && (needs.bundle.result == 'success' || needs.bundle.result == 'skipped') runs-on: ubuntu-latest timeout-minutes: 10 diff --git a/packaging/macos/verify-cask.sh b/packaging/macos/verify-cask.sh index 97d634bb..b70dd9c8 100755 --- a/packaging/macos/verify-cask.sh +++ b/packaging/macos/verify-cask.sh @@ -34,6 +34,7 @@ cp "$tmp/cask.rb" "$(brew --repository "$tap")/Casks/rfirma.rb" brew install --cask "$tap/rfirma" app="$applications/rfirma.app" [ -d "$app" ] || { echo "$app no existe tras instalar" >&2; exit 1; } +xattr -c "$app" "$app/Contents/MacOS/rfirma" --version brew uninstall --cask "$tap/rfirma" diff --git a/packaging/macos/verify-cask.test.sh b/packaging/macos/verify-cask.test.sh index 4fddc04e..57e91207 100755 --- a/packaging/macos/verify-cask.test.sh +++ b/packaging/macos/verify-cask.test.sh @@ -30,15 +30,17 @@ case "$1" in install) cp "$FAKE_REPO/Casks/rfirma.rb" "$FAKE_INSTALLED_CASK" mkdir -p "$APPLICATIONS_DIR/rfirma.app/Contents/MacOS" - printf '#!/bin/sh\necho "rfirma 1.0.0"\n' > "$APPLICATIONS_DIR/rfirma.app/Contents/MacOS/rfirma" + printf '#!/bin/sh\necho "$*" > "$FAKE_LAUNCH_LOG"\n' > "$APPLICATIONS_DIR/rfirma.app/Contents/MacOS/rfirma" chmod +x "$APPLICATIONS_DIR/rfirma.app/Contents/MacOS/rfirma" ;; uninstall) [ -n "${FAKE_KEEP_APP:-}" ] || rm -rf "$APPLICATIONS_DIR/rfirma.app" ;; esac BREW chmod +x "$tmp/bin/brew" +printf '#!/bin/sh\n' > "$tmp/bin/xattr" +chmod +x "$tmp/bin/xattr" -export PATH="$tmp/bin:$PATH" FAKE_REPO="$tmp/repo" FAKE_BREW_LOG="$tmp/brew.log" +export PATH="$tmp/bin:$PATH" FAKE_REPO="$tmp/repo" FAKE_BREW_LOG="$tmp/brew.log" FAKE_LAUNCH_LOG="$tmp/launch.log" export FAKE_INSTALLED_CASK="$tmp/installed.rb" APPLICATIONS_DIR="$tmp/apps" CASK_SOURCE="$tmp/cask.rb" fail() { echo "FALLO $*" >&2; exit 1; } @@ -48,6 +50,7 @@ grep -q '^tap-new --no-git' "$tmp/brew.log" || fail "el tap de usar y tirar debe grep -qF "url \"file://$tmp/rfirma_1.0.0_aarch64.dmg\"" "$tmp/installed.rb" || fail "el cask debe apuntar al dmg local" grep -q "sha256 \"$expected_sha\"" "$tmp/installed.rb" || fail "el cask debe llevar el sha256 del dmg local" grep -q 'app "rfirma.app"' "$tmp/installed.rb" || fail "el resto del cask debe quedar igual" +[ "$(cat "$tmp/launch.log")" = "--version" ] || fail "debe arrancar la app con --version" grep -q '^uninstall --cask' "$tmp/brew.log" || fail "debe desinstalar" [ ! -e "$tmp/apps/rfirma.app" ] || fail "la app debe haberse retirado" From f327581c0902a7ba40d4c148dc42d4d0d5e5188f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sergio=20G=C3=B3mez?= Date: Tue, 20 Oct 2026 11:55:28 +0200 Subject: [PATCH 3/3] =?UTF-8?q?feat(repo):=20el=20=C3=A1rbol=20publicado?= =?UTF-8?q?=20sirve=20el=20.dmg=20de=20macOS=20en=20/macos/?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packaging/repo/build-tree.sh | 7 +++++-- packaging/repo/build-tree.test.sh | 13 ++++++++----- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/packaging/repo/build-tree.sh b/packaging/repo/build-tree.sh index 02e3bb2a..b6c264e6 100755 --- a/packaging/repo/build-tree.sh +++ b/packaging/repo/build-tree.sh @@ -42,7 +42,7 @@ # /apt/ con dists/stable/main/binary-amd64/ # /rpm/ con repodata/ # /windows/ el `-setup.exe` y el `.sig` de cada version, y `latest.json` -# /macos/ el `.app.tar.gz` y el `.sig` de cada version que lo trae, y +# /macos/ el `.dmg`, el `.app.tar.gz` y el `.sig` de cada version que los trae, y # `latest.json` # # APT CON SUITE `stable` Y NO REPOSITORIO PLANO (ID-175): el plano es mas @@ -465,13 +465,16 @@ jq -n \ > "$arbol/windows/latest.json" # ----------------------------------------------------------------- 5. macos -- -# Como `windows/`, con las versiones estables que traen la actualizacion de macOS. +# Como `windows/`, con las versiones estables que traen la actualizacion de macOS y su `.dmg`. echo " macos: colocando las actualizaciones y latest.json" ultima_app="" for i in "${!versiones[@]}"; do [[ "${versiones[$i]}" == *-* ]] && continue [ -n "${app_de[$i]}" ] || continue cp "$serie/${versiones[$i]}/${app_de[$i]}" "$serie/${versiones[$i]}/${app_de[$i]}.sig" "$arbol/macos/" + while IFS= read -r nombre; do + cp "$serie/${versiones[$i]}/$nombre" "$arbol/macos/" + done < <(paquetes_del_formato "$serie/${versiones[$i]}" dmg) ultima_app="$i" done if [ -z "$ultima_app" ]; then diff --git a/packaging/repo/build-tree.test.sh b/packaging/repo/build-tree.test.sh index 1433398e..417da587 100755 --- a/packaging/repo/build-tree.test.sh +++ b/packaging/repo/build-tree.test.sh @@ -81,12 +81,13 @@ actualizacion_de_macos_de_pega() { local dir="$1" version="$2" echo "app $version" > "$dir/rFirma_${version}_aarch64.app.tar.gz" printf 'firma mac de %s\n' "$version" > "$dir/rFirma_${version}_aarch64.app.tar.gz.sig" + echo "dmg $version" > "$dir/rFirma_${version}_aarch64.dmg" "$raiz/scripts/packages-manifest.sh" write "$dir" } sin_actualizacion_de_macos() { local dir="$1" - rm -f "$dir"/*.app.tar.gz "$dir"/*.app.tar.gz.sig + rm -f "$dir"/*.app.tar.gz "$dir"/*.app.tar.gz.sig "$dir"/*.dmg jq 'map(select(.platform != "macos"))' "$dir/paquetes.json" > "$dir/paquetes.json.nuevo" mv "$dir/paquetes.json.nuevo" "$dir/paquetes.json" } @@ -289,6 +290,7 @@ echo "de mas" > "$serie/v0.4.10/rFirma_9.9.9_x64-setup.exe.sig" echo "de mas" > "$serie/v0.4.10/subido-a-mano.exe" echo "de mas" > "$serie/v0.4.10/rFirma_9.9.9_aarch64.app.tar.gz" echo "de mas" > "$serie/v0.4.10/rFirma_9.9.9_aarch64.app.tar.gz.sig" +echo "de mas" > "$serie/v0.4.10/rFirma_9.9.9_aarch64.dmg" # --------------------------------------------------------------------------- # El arbol, dos veces y en dos directorios distintos @@ -506,16 +508,17 @@ fi # --------------------------------------------------------------------------- # /macos/: la actualizacion y latest.json # --------------------------------------------------------------------------- -# macos_has_the_update_and_signature_of_every_version_that_brings_it +# macos_has_the_dmg_update_and_signature_of_every_version_that_brings_them faltan_de_macos=0 for version in 0.4.2 0.4.10; do - for fichero in "rFirma_${version}_aarch64.app.tar.gz" "rFirma_${version}_aarch64.app.tar.gz.sig"; do + for fichero in "rFirma_${version}_aarch64.dmg" "rFirma_${version}_aarch64.app.tar.gz" "rFirma_${version}_aarch64.app.tar.gz.sig"; do [ -f "$tmp/arbol/macos/$fichero" ] || { faltan_de_macos=1; echo " falta $fichero" >&2; } done done if [ "$faltan_de_macos" -eq 0 ] && [ -z "$(find "$tmp/arbol/macos" -name '*0.4.0*' -print -quit)" ] \ - && [ ! -e "$tmp/arbol/macos/rFirma_9.9.9_aarch64.app.tar.gz" ]; then - ok "macos/ trae la actualizacion y su .sig de cada version que la trae, y nada fuera del manifiesto" + && [ ! -e "$tmp/arbol/macos/rFirma_9.9.9_aarch64.app.tar.gz" ] \ + && [ ! -e "$tmp/arbol/macos/rFirma_9.9.9_aarch64.dmg" ]; then + ok "macos/ trae el .dmg, la actualizacion y su .sig de cada version que los trae, y nada fuera del manifiesto" else fail "macos/ no trae lo que dicen los manifiestos de la serie" fi