fix(release): do not vendor static archives as loadable plug-ins #76
Workflow file for this run
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: release | |
| on: | |
| push: | |
| tags: | |
| - 'v*' | |
| workflow_dispatch: | |
| permissions: | |
| contents: read | |
| env: | |
| RUST_LOG: info | |
| concurrency: | |
| group: release-${{ github.ref }} | |
| cancel-in-progress: true | |
| jobs: | |
| # ── Linux ───────────────────────────────────────────────────────────── | |
| build-linux: | |
| name: build Linux ${{ matrix.arch }} | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - arch: x86_64 | |
| runner: ubuntu-22.04 | |
| target: x86_64-unknown-linux-gnu | |
| - arch: aarch64 | |
| runner: ubuntu-22.04-arm | |
| target: aarch64-unknown-linux-gnu | |
| runs-on: ${{ matrix.runner }} | |
| timeout-minutes: 120 | |
| env: | |
| CARGO_TERM_COLOR: always | |
| RUST_BACKTRACE: 1 | |
| TMPDIR: ${{ github.workspace }}/tmp | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| with: | |
| fetch-depth: 0 | |
| - name: Install system dependencies (apt) | |
| run: scripts/ci/setup-linux.sh release | |
| - name: Prepare workspace temp directory | |
| run: mkdir -p "$TMPDIR" | |
| - name: Set up Rust tooling | |
| uses: ./.github/actions/setup-rust | |
| with: | |
| cache-key: release-linux-${{ matrix.arch }} | |
| target: ${{ matrix.target }} | |
| - name: Install linuxdeploy | |
| uses: AnimMouse/setup-appimage@c8debfb91e47b03bf4bdc78a4b22fe14ebfaf7f5 # v2 | |
| with: | |
| filename: linuxdeploy | |
| url: https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-${{ matrix.arch }}.AppImage | |
| cache_key: continuous-${{ matrix.arch }} | |
| - name: Install appimagetool | |
| uses: AnimMouse/setup-appimage@c8debfb91e47b03bf4bdc78a4b22fe14ebfaf7f5 # v2 | |
| with: | |
| filename: appimagetool | |
| url: https://github.com/AppImage/appimagetool/releases/download/continuous/appimagetool-${{ matrix.arch }}.AppImage | |
| cache_key: continuous-${{ matrix.arch }} | |
| - name: Build release binaries | |
| run: cargo xtask fresh-build --release --features neomacs-layout-engine/freetype-bundled | |
| - name: Set version env | |
| run: echo "VERSION=${GITHUB_REF_NAME#v}" >> "$GITHUB_ENV" | |
| - name: Package tarball | |
| run: | | |
| # package-release.sh builds the canonical release tree | |
| # (bin/ + share/neomacs/) and its tarball: the archive is runnable | |
| # in place and install.sh consumes the same layout. | |
| ./scripts/package-release.sh \ | |
| --target "${{ matrix.target }}" \ | |
| --skip-build --no-smoke | |
| - name: Package AppImage | |
| run: | | |
| ./scripts/package-appimage.sh \ | |
| --target "${{ matrix.target }}" \ | |
| --skip-build --no-smoke | |
| - name: Package .deb | |
| run: | | |
| ./scripts/package-deb.sh \ | |
| --target "${{ matrix.target }}" \ | |
| --skip-build --no-smoke | |
| - name: Package .rpm | |
| run: | | |
| ./scripts/package-rpm.sh \ | |
| --target "${{ matrix.target }}" \ | |
| --skip-build --no-smoke | |
| - name: Verify packaged artifacts and GLIBC baseline | |
| run: | | |
| ./scripts/test-linux-release-artifacts.sh \ | |
| --target "${{ matrix.target }}" \ | |
| --tar-version "$VERSION" | |
| - name: Upload Linux artifacts | |
| uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 | |
| with: | |
| name: linux-${{ matrix.arch }} | |
| path: | | |
| dist/*.tar.gz | |
| dist/*.AppImage | |
| dist/*.deb | |
| dist/*.rpm | |
| if-no-files-found: error | |
| # ── macOS aarch64 (Apple Silicon) ───────────────────────────────────── | |
| build-macos-aarch64: | |
| name: build macOS aarch64 | |
| # Pin the image so a release does not change underneath us when GitHub | |
| # advances `macos-latest`. GitHub's standard macos-15 image is arm64. | |
| runs-on: macos-15 | |
| timeout-minutes: 180 | |
| env: | |
| CARGO_TERM_COLOR: always | |
| RUST_BACKTRACE: 1 | |
| GSTREAMER_VERSION: 1.26.9 | |
| # vendor-macos-runtime.sh rewrites every non-system load command to | |
| # @executable_path/../Frameworks/..., which is LONGER than the original | |
| # install name. Mach-O has no room to grow those load commands unless | |
| # the linker reserved it, so install_name_tool failed on mock-display | |
| # with "larger updated load commands do not fit (the program must be | |
| # relinked, and you may need to use -headerpad_max_install_names)". | |
| # Reserve the space for every binary that goes into the bundle -- the | |
| # others only fit by accident of their existing padding. | |
| RUSTFLAGS: "-C link-arg=-Wl,-headerpad_max_install_names" | |
| outputs: | |
| distribution_mode: ${{ steps.macos-signing.outputs.distribution_mode }} | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| with: | |
| fetch-depth: 0 | |
| - name: Restore GStreamer SDK package cache | |
| id: cache-macos-gstreamer | |
| uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 | |
| with: | |
| path: tmp/gstreamer-macos | |
| key: macos-gstreamer-${{ env.GSTREAMER_VERSION }}-v1 | |
| - name: Install pinned GStreamer SDK | |
| run: ./scripts/setup-macos-gstreamer.sh | |
| - name: Save GStreamer SDK package cache | |
| if: steps.cache-macos-gstreamer.outputs.cache-hit != 'true' | |
| uses: actions/cache/save@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 | |
| with: | |
| path: tmp/gstreamer-macos | |
| key: macos-gstreamer-${{ env.GSTREAMER_VERSION }}-v1 | |
| - name: Set up Rust tooling | |
| uses: ./.github/actions/setup-rust | |
| with: | |
| cache-key: release-macos-aarch64 | |
| target: aarch64-apple-darwin | |
| - name: Set version env | |
| run: echo "VERSION=${GITHUB_REF_NAME#v}" >> "$GITHUB_ENV" | |
| - name: Build release binaries | |
| # fresh-build EXECUTES neomacs-temacs to produce the dump, and that | |
| # happens before the .app exists, so vendor-macos-runtime.sh cannot | |
| # help it. setup-macos-gstreamer.sh exports only PKG_CONFIG_PATH, | |
| # which the linker uses, so temacs died with | |
| # `dyld: Library not loaded: @rpath/libgstvideo-1.0.0.dylib`. | |
| # Build-time only: it changes no load command, so the shipped bundle | |
| # is still whatever vendor-macos-runtime.sh makes it. Set inline | |
| # rather than via $GITHUB_ENV because SIP strips DYLD_* when /bin/bash | |
| # (the shell of every run: step) is exec'd; bash then execs cargo, | |
| # which is not protected, so it reaches temacs. | |
| run: | | |
| gst_libdir="$(pkg-config --variable=libdir gstreamer-1.0)" | |
| test -n "$gst_libdir" || { echo "gstreamer-1.0 has no libdir" >&2; exit 1; } | |
| DYLD_FALLBACK_LIBRARY_PATH="$gst_libdir${DYLD_FALLBACK_LIBRARY_PATH:+:$DYLD_FALLBACK_LIBRARY_PATH}" \ | |
| cargo xtask fresh-build --release --features neomacs-layout-engine/freetype-bundled | |
| - name: Configure Developer ID signing and notarization | |
| id: macos-signing | |
| env: | |
| # Developer ID is an optional trust upgrade. Set this repository | |
| # variable to 1 only after all five secrets below are configured. | |
| MACOS_REQUIRE_SIGNING: ${{ vars.MACOS_REQUIRE_SIGNING || '0' }} | |
| MACOS_CERTIFICATE_P12_BASE64: ${{ secrets.MACOS_CERTIFICATE_P12_BASE64 }} | |
| MACOS_CERTIFICATE_PASSWORD: ${{ secrets.MACOS_CERTIFICATE_PASSWORD }} | |
| APPLE_NOTARY_KEY_P8_BASE64: ${{ secrets.APPLE_NOTARY_KEY_P8_BASE64 }} | |
| APPLE_NOTARY_KEY_ID: ${{ secrets.APPLE_NOTARY_KEY_ID }} | |
| APPLE_NOTARY_ISSUER_ID: ${{ secrets.APPLE_NOTARY_ISSUER_ID }} | |
| run: ./scripts/configure-macos-signing-ci.sh | |
| - name: Build and package complete macOS application | |
| run: ./scripts/package-macos-app.sh --skip-build | |
| - name: Upload notarization diagnostics | |
| if: failure() | |
| uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 | |
| with: | |
| name: macos-notarization-diagnostics | |
| path: dist/notary/*.json | |
| if-no-files-found: ignore | |
| - name: Upload macOS aarch64 artifacts | |
| uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 | |
| with: | |
| name: macos-aarch64 | |
| path: | | |
| dist/*.dmg | |
| dist/*.zip | |
| dist/*.tar.gz | |
| if-no-files-found: error | |
| # Test on a second, clean machine so the SDK installed by the build cannot | |
| # satisfy an accidentally external dependency or runtime resource lookup. | |
| verify-macos-aarch64: | |
| name: verify macOS aarch64 distribution | |
| needs: build-macos-aarch64 | |
| runs-on: macos-15 | |
| timeout-minutes: 60 | |
| env: | |
| # Verify Gatekeeper/notary state only when the build actually used a | |
| # Developer ID. Ad-hoc releases still receive signature, dependency, | |
| # resource, container-integrity, and clean-machine launch verification. | |
| MACOS_DISTRIBUTION_MODE: ${{ needs.build-macos-aarch64.outputs.distribution_mode }} | |
| steps: | |
| - name: Checkout verification scripts outside the build-time source path | |
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| with: | |
| path: verification-source | |
| - name: Download the distributed macOS artifacts | |
| uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 | |
| with: | |
| name: macos-aarch64 | |
| path: verification-source/dist | |
| - name: Verify every distributed macOS artifact | |
| working-directory: verification-source | |
| run: | | |
| ./scripts/test-macos-release-artifact.sh \ | |
| dist/*.dmg \ | |
| dist/*.zip \ | |
| dist/*.tar.gz | |
| - name: Verify install.sh from the published release | |
| run: | | |
| # Same user path as the Linux verify-install-script job: fetch the | |
| # installer asset from the just-published release and let its own | |
| # post-install check batch-start the app bundle's binary through | |
| # the ~/.local/bin symlink (no environment variables). | |
| curl -fsSL \ | |
| "https://github.com/eval-exec/neomacs/releases/download/${GITHUB_REF_NAME}/install.sh" \ | |
| -o install.sh | |
| sh install.sh --tag "${GITHUB_REF_NAME}" | |
| test -x "$HOME/.local/bin/neomacs" | |
| env -u NEOMACS_RUNTIME_ROOT \ | |
| "$HOME/.local/bin/neomacs" --batch --eval '(princ (emacs-version))' | |
| # ── Windows ─────────────────────────────────────────────────────────── | |
| build-windows: | |
| name: build Windows ${{ matrix.arch }} | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - arch: x86_64 | |
| runner: windows-latest | |
| target: x86_64-pc-windows-msvc | |
| gstreamer_arch: x86_64 | |
| - arch: aarch64 | |
| runner: windows-11-arm | |
| target: aarch64-pc-windows-msvc | |
| gstreamer_arch: arm64 | |
| runs-on: ${{ matrix.runner }} | |
| timeout-minutes: 180 | |
| env: | |
| CARGO_TERM_COLOR: always | |
| RUST_BACKTRACE: 1 | |
| GSTREAMER_VERSION: 1.28.6 | |
| GSTREAMER_ARCH: ${{ matrix.gstreamer_arch }} | |
| NEOMACS_RELEASE_TARGET: ${{ matrix.target }} | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| with: | |
| fetch-depth: 0 | |
| - name: Restore GStreamer SDK cache | |
| id: cache-gstreamer | |
| uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 | |
| with: | |
| path: | | |
| C:\gstreamer | |
| C:\gstreamer-installer-cache | |
| key: windows-gstreamer-msvc-${{ matrix.gstreamer_arch }}-${{ env.GSTREAMER_VERSION }}-v1 | |
| - name: Install GStreamer SDK | |
| if: steps.cache-gstreamer.outputs.cache-hit != 'true' | |
| shell: powershell | |
| run: ./scripts/setup-windows-gstreamer.ps1 -Install | |
| - name: Save GStreamer SDK cache | |
| if: steps.cache-gstreamer.outputs.cache-hit != 'true' | |
| uses: actions/cache/save@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 | |
| with: | |
| path: | | |
| C:\gstreamer | |
| C:\gstreamer-installer-cache | |
| key: ${{ steps.cache-gstreamer.outputs.cache-primary-key }} | |
| - name: Configure GStreamer SDK | |
| shell: powershell | |
| run: ./scripts/setup-windows-gstreamer.ps1 | |
| - name: Set up Rust tooling | |
| uses: ./.github/actions/setup-rust | |
| with: | |
| cache-key: release-windows-msvc-${{ matrix.arch }} | |
| target: ${{ matrix.target }} | |
| - name: Install NSIS | |
| run: choco install nsis -y | |
| - name: Add NSIS to PATH | |
| shell: powershell | |
| run: Add-Content -Path $env:GITHUB_PATH -Value "C:\Program Files (x86)\NSIS" | |
| - name: Verify GStreamer pkg-config | |
| shell: bash | |
| run: source scripts/windows-gstreamer-env.sh --verify | |
| - name: Build release binaries | |
| shell: bash | |
| timeout-minutes: 150 | |
| run: | | |
| source scripts/windows-gstreamer-env.sh | |
| # The pdump generation runs neomacs, whose startup expands `~`. The | |
| # minimal CI build environment sets USERPROFILE/APPDATA but not HOME, | |
| # so `~` would stay literal and `directory-files "~"` would fail. Give | |
| # it HOME explicitly (a real Windows session always has one; GNU's | |
| # w32.c init_environment likewise guarantees HOME is set). | |
| export HOME="${USERPROFILE:-$GITHUB_WORKSPACE}" | |
| cargo xtask fresh-build --release --features neomacs-layout-engine/freetype-bundled | |
| - name: Set version env (bash) | |
| shell: bash | |
| run: echo "VERSION=${GITHUB_REF_NAME#v}" >> "$GITHUB_ENV" | |
| - name: Package zip | |
| shell: bash | |
| run: | | |
| TARGET="$NEOMACS_RELEASE_TARGET" | |
| STAGING="neomacs-${VERSION}-${TARGET}" | |
| mkdir -p "$STAGING" | |
| cp target/release/neomacs.exe "$STAGING/" | |
| cp target/release/neomacsclient.exe "$STAGING/" | |
| cp target/release/neomacs.pdump "$STAGING/" | |
| cp -r lisp "$STAGING/" | |
| cp -r etc "$STAGING/" | |
| cp COPYING "$STAGING/" | |
| ./scripts/vendor-windows-gstreamer-runtime.sh \ | |
| --package-root "$STAGING" \ | |
| --bin-dir "$STAGING" | |
| mkdir -p dist | |
| 7z a -tzip "dist/${STAGING}.zip" "$STAGING" | |
| - name: Package .exe installer | |
| shell: bash | |
| timeout-minutes: 20 | |
| run: | | |
| source scripts/windows-gstreamer-env.sh | |
| ./scripts/package-windows-installer.sh \ | |
| --target "$NEOMACS_RELEASE_TARGET" \ | |
| --skip-build --no-smoke | |
| - name: Verify Windows installer ownership contract | |
| shell: powershell | |
| timeout-minutes: 10 | |
| run: | | |
| ./scripts/run-windows-installer-contract.ps1 ` | |
| -Architecture "${{ matrix.arch }}" ` | |
| -ConfirmEphemeralRunner | |
| - name: Upload Windows artifacts | |
| uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 | |
| with: | |
| name: windows-${{ matrix.arch }} | |
| path: | | |
| dist/*.zip | |
| dist/*.exe | |
| if-no-files-found: error | |
| # ── GitHub Release ──────────────────────────────────────────────────── | |
| create-release: | |
| name: create release | |
| permissions: | |
| contents: write | |
| discussions: write | |
| needs: | |
| - build-linux | |
| - build-macos-aarch64 | |
| - verify-macos-aarch64 | |
| - build-windows | |
| runs-on: ubuntu-22.04 | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| - name: Download all artifacts | |
| uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 | |
| with: | |
| path: dist | |
| merge-multiple: true | |
| - name: Stage the installer and its checksum manifest | |
| run: | | |
| # install.sh ships as a release asset so that | |
| # https://github.com/eval-exec/neomacs/releases/latest/download/install.sh | |
| # always serves the installer that matches the assets it downloads | |
| # (the neomacs.org entry point fetches exactly that URL). | |
| install -m 0644 install.sh dist/install.sh | |
| # The glob expands before SHA256SUMS exists, so the manifest covers | |
| # every asset (and the installer) but never itself. | |
| cd dist | |
| sha256sum ./* > SHA256SUMS | |
| - name: Create GitHub Release | |
| uses: softprops/action-gh-release@3bb12739c298aeb8a4eeaf626c5b8d85266b0e65 # v2 | |
| with: | |
| name: NEO Emacs ${{ github.ref_name }} | |
| files: dist/* | |
| generate_release_notes: true | |
| discussion_category_name: Announcements | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| # ── Install script verification ─────────────────────────────────────── | |
| # Installs through the public curl|bash entry exactly as a user would: | |
| # the installer asset is downloaded from the just-published release, and | |
| # its own post-install check batch-starts the binary with no environment | |
| # variables, exercising the runtime-root resolution through the | |
| # ~/.local/bin symlink chain. | |
| verify-install-script: | |
| name: verify install.sh from the published release | |
| needs: create-release | |
| runs-on: ubuntu-24.04 | |
| timeout-minutes: 20 | |
| env: | |
| prefix: ${{ github.workspace }}/tmp/prefix | |
| steps: | |
| - name: Run the published installer | |
| run: | | |
| curl -fsSL \ | |
| "https://github.com/eval-exec/neomacs/releases/download/${GITHUB_REF_NAME}/install.sh" \ | |
| -o install.sh | |
| sh install.sh --tag "${GITHUB_REF_NAME}" --prefix "$prefix" | |
| - name: Assert the installed layout | |
| run: | | |
| version="${GITHUB_REF_NAME#v}" | |
| test -x "$prefix/bin/neomacs" | |
| test "$(readlink "$prefix/share/neomacs/current")" = "versions/$version" | |
| test "$(readlink "$prefix/bin/neomacs")" = "../share/neomacs/current/bin/neomacs" | |
| exe="$(readlink -f "$prefix/bin/neomacs")" | |
| test -f "$(dirname "$exe")/neomacs.pdump" | |
| test -d "$(dirname "$exe")/../share/neomacs/lisp" | |
| env -u NEOMACS_RUNTIME_ROOT \ | |
| "$prefix/bin/neomacs" --batch --eval '(princ (emacs-version))' |