deb packaging #45
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: build | |
| on: | |
| push: | |
| branches: ["**"] | |
| tags: ["v*"] | |
| pull_request: | |
| workflow_dispatch: | |
| permissions: | |
| contents: write | |
| env: | |
| LIBTCD_URL: https://flaterco.com/files/xtide/libtcd-2.2.7-r3.tar.xz | |
| HARMONICS_URL: https://flaterco.com/files/xtide/harmonics-dwf-20251228-free.tar.xz | |
| XTIDE_WIN64_ZIP_URL: https://flaterco.com/files/xtide/xtide-2.15-win64.zip | |
| jobs: | |
| linux: | |
| name: linux (${{ matrix.arch }}) | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - arch: amd64 | |
| runs_on: ubuntu-latest | |
| tar_suffix: linux-amd64 | |
| - arch: arm64 | |
| runs_on: ubuntu-24.04-arm | |
| tar_suffix: linux-arm64 | |
| runs-on: ${{ matrix.runs_on }} | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Compute artifact tag + deb version | |
| run: | | |
| set -euo pipefail | |
| if [[ "${GITHUB_REF}" == refs/tags/* ]]; then | |
| echo "ARTIFACT_TAG=${GITHUB_REF_NAME}" >> "$GITHUB_ENV" | |
| echo "DEB_VERSION=${GITHUB_REF_NAME#v}-1" >> "$GITHUB_ENV" | |
| else | |
| echo "ARTIFACT_TAG=sha-${GITHUB_SHA::7}" >> "$GITHUB_ENV" | |
| echo "DEB_VERSION=0.0.0+sha${GITHUB_SHA::7}-1" >> "$GITHUB_ENV" | |
| fi | |
| echo "DEB_ARCH=$(dpkg --print-architecture)" >> "$GITHUB_ENV" | |
| - name: Install deps | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y \ | |
| cmake ninja-build pkg-config g++ \ | |
| curl tar xz-utils make \ | |
| dpkg-dev fakeroot apt-utils patchelf | |
| - name: Build + install libtcd (prefix) | |
| run: | | |
| set -euo pipefail | |
| rm -rf _deps && mkdir -p _deps | |
| cd _deps | |
| curl -L --retry 3 --retry-delay 2 -o libtcd.tar.xz "${LIBTCD_URL}" | |
| tar -xf libtcd.tar.xz | |
| LIBTCD_DIR="$(find . -maxdepth 1 -type d -name 'libtcd-*' | head -n1)" | |
| PREFIX="$PWD/libtcd-prefix" | |
| cd "$LIBTCD_DIR" | |
| ./configure --prefix="$PREFIX" | |
| make -j | |
| make install | |
| echo "TCD_PREFIX=$PREFIX" >> "$GITHUB_ENV" | |
| - name: Configure (release) | |
| run: | | |
| cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release \ | |
| -DCMAKE_PREFIX_PATH="${{ env.TCD_PREFIX }}" | |
| - name: Build | |
| run: | | |
| cmake --build build -j | |
| - name: Download harmonics (.tcd) for smoke test | |
| run: | | |
| set -euo pipefail | |
| rm -rf _deps/harmonics | |
| mkdir -p _deps/harmonics | |
| cd _deps/harmonics | |
| curl -L --retry 3 --retry-delay 2 -o harmonics.tar.xz "${HARMONICS_URL}" | |
| tar -xf harmonics.tar.xz | |
| TCD_FILE="$(find . -type f -name '*.tcd' | head -n1)" | |
| if [ -z "${TCD_FILE}" ]; then | |
| echo "ERROR: No .tcd found after extracting harmonics archive" | |
| find . -maxdepth 3 -type f | head -n 50 || true | |
| exit 1 | |
| fi | |
| echo "HARM_TCD=$PWD/${TCD_FILE#./}" >> "$GITHUB_ENV" | |
| echo "Using HARM_TCD=$PWD/${TCD_FILE#./}" | |
| - name: Smoke test (Linux) | |
| run: | | |
| set -euo pipefail | |
| LAT="40.7050" | |
| LON="-74.0120" | |
| echo "=== xtide-nearest --help ===" | |
| ./build/xtide-nearest --help | head -n 60 || true | |
| echo "" | |
| echo "=== Nearest TIDE station (lat=${LAT}, lon=${LON}) ===" | |
| ./build/xtide-nearest --lat "${LAT}" --lon "${LON}" --tcd "${{ env.HARM_TCD }}" \ | |
| nearest --no-current --top 1 | |
| echo "" | |
| echo "=== Nearest CURRENT station (lat=${LAT}, lon=${LON}) ===" | |
| ./build/xtide-nearest --lat "${LAT}" --lon "${LON}" --tcd "${{ env.HARM_TCD }}" \ | |
| nearest --no-tide --top 1 | |
| - name: Package (tar.gz) | |
| run: | | |
| set -euo pipefail | |
| rm -rf dist | |
| mkdir -p dist | |
| cp -f build/xtide-nearest dist/ | |
| [ -f README.md ] && cp -f README.md dist/ || true | |
| [ -f LICENSE ] && cp -f LICENSE dist/ || true | |
| tar -czf xtide-nearest-${{ env.ARTIFACT_TAG }}-${{ matrix.tar_suffix }}.tar.gz -C dist . | |
| - name: Package (Debian .deb, bundles libtcd privately) | |
| run: | | |
| set -euo pipefail | |
| PKG="xtide-nearest" | |
| INSTALL_ROOT="/usr/lib/${PKG}" | |
| STAGE="debian/${PKG}" | |
| rm -rf debian | |
| install -d \ | |
| "$STAGE/DEBIAN" \ | |
| "$STAGE/usr/bin" \ | |
| "$STAGE${INSTALL_ROOT}/lib" \ | |
| "$STAGE/usr/share/doc/$PKG" | |
| # App under private prefix | |
| install -m 0755 build/xtide-nearest "$STAGE${INSTALL_ROOT}/xtide-nearest" | |
| # Bundle libtcd into private prefix (no global /usr/lib install => no conflicts) | |
| if [ -e "${{ env.TCD_PREFIX }}/lib/libtcd.so.1" ]; then | |
| cp -a "${{ env.TCD_PREFIX }}/lib/libtcd.so.1"* "$STAGE${INSTALL_ROOT}/lib/" | |
| elif ls "${{ env.TCD_PREFIX }}/lib/libtcd.so"* >/dev/null 2>&1; then | |
| cp -a "${{ env.TCD_PREFIX }}/lib/libtcd.so"* "$STAGE${INSTALL_ROOT}/lib/" | |
| else | |
| echo "ERROR: libtcd shared library not found under ${{ env.TCD_PREFIX }}/lib" | |
| ls -la "${{ env.TCD_PREFIX }}/lib" || true | |
| exit 1 | |
| fi | |
| # Correct RPATH for this layout: | |
| # binary: .../usr/lib/xtide-nearest/xtide-nearest | |
| # libs: .../usr/lib/xtide-nearest/lib/libtcd.so.1 | |
| # => $ORIGIN/lib | |
| patchelf --set-rpath '$ORIGIN/lib' "$STAGE${INSTALL_ROOT}/xtide-nearest" | |
| # /usr/bin wrapper | |
| cat > "$STAGE/usr/bin/$PKG" <<'EOF' | |
| #!/bin/sh | |
| set -e | |
| : "${XTIDE_NEAREST_HOME:=/usr/lib/xtide-nearest}" | |
| exec "$XTIDE_NEAREST_HOME/xtide-nearest" "$@" | |
| EOF | |
| chmod 0755 "$STAGE/usr/bin/$PKG" | |
| # Optional docs | |
| [ -f README.md ] && install -m 0644 README.md "$STAGE/usr/share/doc/$PKG/README.md" || true | |
| [ -f LICENSE ] && install -m 0644 LICENSE "$STAGE/usr/share/doc/$PKG/copyright" || true | |
| SIZE_KB="$(du -sk "$STAGE/usr" | awk '{print $1}')" | |
| # Minimal debian/control so dpkg-shlibdeps can compute system Depends | |
| cat > debian/control <<EOF | |
| Source: $PKG | |
| Section: utils | |
| Priority: optional | |
| Maintainer: ${GITHUB_REPOSITORY} <noreply@github.com> | |
| Standards-Version: 4.6.2 | |
| Package: $PKG | |
| Architecture: any | |
| Depends: \${shlibs:Depends}, \${misc:Depends} | |
| Description: xtide-nearest command-line tool | |
| Find nearest tide/current station from an XTide harmonics database. | |
| EOF | |
| # Compute Depends from the staged binary. | |
| # -l points dpkg-shlibdeps at the private in-package lib dir so libtcd is treated as "provided" | |
| # System libs (libstdc++6, libgcc-s1, libc6, etc.) stay as Depends. | |
| DEPENDS="$(dpkg-shlibdeps --ignore-missing-info -O -p"$PKG" \ | |
| -l"$STAGE${INSTALL_ROOT}/lib" \ | |
| -S"$STAGE" \ | |
| -e "$STAGE${INSTALL_ROOT}/xtide-nearest" | sed -n 's/^shlibs:Depends=//p')" | |
| [ -n "$DEPENDS" ] || DEPENDS="libc6" | |
| # Final binary package control | |
| cat > "$STAGE/DEBIAN/control" <<EOF | |
| Package: $PKG | |
| Version: ${DEB_VERSION} | |
| Section: utils | |
| Priority: optional | |
| Architecture: ${DEB_ARCH} | |
| Maintainer: ${GITHUB_REPOSITORY} <noreply@github.com> | |
| Depends: $DEPENDS | |
| Installed-Size: $SIZE_KB | |
| Homepage: https://github.com/${GITHUB_REPOSITORY} | |
| Description: xtide-nearest command-line tool | |
| Find nearest tide/current station from an XTide harmonics database. | |
| EOF | |
| fakeroot dpkg-deb --build "$STAGE" "${PKG}_${DEB_VERSION}_${DEB_ARCH}.deb" | |
| - name: Upload artifact | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: linux-${{ matrix.arch }} | |
| path: | | |
| xtide-nearest-${{ env.ARTIFACT_TAG }}-${{ matrix.tar_suffix }}.tar.gz | |
| xtide-nearest_${{ env.DEB_VERSION }}_${{ env.DEB_ARCH }}.deb | |
| windows-x64: | |
| name: windows (x64) | |
| runs-on: windows-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Set up MSYS2 (MINGW64) | |
| uses: msys2/setup-msys2@v2 | |
| with: | |
| msystem: MINGW64 | |
| update: true | |
| install: >- | |
| mingw-w64-x86_64-gcc | |
| mingw-w64-x86_64-cmake | |
| mingw-w64-x86_64-ninja | |
| mingw-w64-x86_64-pkgconf | |
| make | |
| curl | |
| tar | |
| unzip | |
| xz | |
| - name: Compute artifact tag | |
| shell: msys2 {0} | |
| run: | | |
| if [[ "${GITHUB_REF}" == refs/tags/* ]]; then | |
| echo "ARTIFACT_TAG=${GITHUB_REF_NAME}" >> "$GITHUB_ENV" | |
| else | |
| echo "ARTIFACT_TAG=sha-${GITHUB_SHA::7}" >> "$GITHUB_ENV" | |
| fi | |
| - name: Build + install libtcd (prefix) | |
| shell: msys2 {0} | |
| run: | | |
| set -euo pipefail | |
| rm -rf _deps && mkdir -p _deps | |
| cd _deps | |
| curl -L --retry 3 --retry-delay 2 -o libtcd.tar.xz "${LIBTCD_URL}" | |
| tar -xf libtcd.tar.xz | |
| LIBTCD_DIR="$(find . -maxdepth 1 -type d -name 'libtcd-*' | head -n1)" | |
| PREFIX="$PWD/libtcd-prefix" | |
| cd "$LIBTCD_DIR" | |
| ./configure --prefix="$PREFIX" | |
| make -j | |
| make install | |
| echo "TCD_PREFIX=$PREFIX" >> "$GITHUB_ENV" | |
| - name: Configure (release) | |
| shell: msys2 {0} | |
| run: | | |
| set -euo pipefail | |
| cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release \ | |
| -DCMAKE_PREFIX_PATH="${{ env.TCD_PREFIX }}" | |
| - name: Build | |
| shell: msys2 {0} | |
| run: | | |
| cmake --build build -j | |
| - name: Download harmonics (.tcd) for smoke test | |
| shell: msys2 {0} | |
| run: | | |
| set -euo pipefail | |
| rm -rf _deps/harmonics | |
| mkdir -p _deps/harmonics | |
| cd _deps/harmonics | |
| curl -L --retry 3 --retry-delay 2 -o harmonics.tar.xz "${HARMONICS_URL}" | |
| tar -xf harmonics.tar.xz || true | |
| TCD_FILE="$(find . -type f -name '*.tcd' | head -n1)" | |
| if [ -z "${TCD_FILE}" ]; then | |
| echo "ERROR: No .tcd found after extracting harmonics archive" | |
| find . -maxdepth 4 -type f | head -n 200 || true | |
| exit 1 | |
| fi | |
| echo "HARM_TCD=$PWD/${TCD_FILE#./}" >> "$GITHUB_ENV" | |
| echo "Using HARM_TCD=$PWD/${TCD_FILE#./}" | |
| - name: Download XTide Windows bundle (for tide.exe) | |
| shell: msys2 {0} | |
| run: | | |
| set -euo pipefail | |
| rm -rf _deps/xtide-win | |
| mkdir -p _deps/xtide-win | |
| curl -L --retry 3 --retry-delay 2 -o _deps/xtide-win.zip "${XTIDE_WIN64_ZIP_URL}" | |
| unzip -q _deps/xtide-win.zip -d _deps/xtide-win | |
| TIDE_EXE="$(find _deps/xtide-win -iname 'tide.exe' | head -n1)" | |
| if [ ! -f "$TIDE_EXE" ]; then | |
| echo "ERROR: tide.exe not found inside XTide bundle" | |
| find _deps/xtide-win -maxdepth 4 -type f | head -n 100 || true | |
| exit 1 | |
| fi | |
| TIDE_EXE_WIN="$(cygpath -w "$TIDE_EXE")" | |
| printf 'TIDE_EXE=%s\n' "$TIDE_EXE" >> "$GITHUB_ENV" | |
| printf 'TIDE_EXE_WIN=%s\n' "$TIDE_EXE_WIN" >> "$GITHUB_ENV" | |
| echo "Using TIDE_EXE=$TIDE_EXE" | |
| echo "Using TIDE_EXE_WIN=$TIDE_EXE_WIN" | |
| - name: Smoke test (Windows) | |
| shell: msys2 {0} | |
| run: | | |
| set -euo pipefail | |
| LAT="40.7050" | |
| LON="-74.0120" | |
| echo "=== Nearest TIDE station (lat=${LAT}, lon=${LON}) ===" | |
| ./build/xtide-nearest.exe --lat "${LAT}" --lon "${LON}" --tcd "${{ env.HARM_TCD }}" \ | |
| nearest --no-current --top 1 | |
| echo "" | |
| echo "=== Nearest CURRENT station (lat=${LAT}, lon=${LON}) ===" | |
| ./build/xtide-nearest.exe --lat "${LAT}" --lon "${LON}" --tcd "${{ env.HARM_TCD }}" \ | |
| nearest --no-tide --top 1 | |
| echo "" | |
| echo "=== tide.exe help (sanity) ===" | |
| "${{ env.TIDE_EXE }}" -help 2>/dev/null | head -n 20 || true | |
| export HFILE_PATH="$(dirname "${{ env.HARM_TCD }}")" | |
| echo "HFILE_PATH=$HFILE_PATH" | |
| STATION='Dimond Reef (depth 11 ft), Upper Bay, New York Harbor, New York Current' | |
| echo "" | |
| echo "=== Predict CURRENT via tide.exe (CSV, head) ===" | |
| "${{ env.TIDE_EXE }}" -l "$STATION" \ | |
| -b "2026-01-22 00:00" \ | |
| -e "2026-01-22 06:00" \ | |
| -m r -f c -s "00:30" -z n -em pSsMm \ | |
| > out.csv | |
| echo "--- out.csv (head) ---" | |
| sed -n '1,12p' out.csv | |
| test "$(wc -l < out.csv)" -ge 5 | |
| - name: Package zip | |
| shell: msys2 {0} | |
| run: | | |
| set -euo pipefail | |
| rm -rf dist xtide-nearest-${{ env.ARTIFACT_TAG }}-windows-x64.zip | |
| mkdir -p dist | |
| cp -f build/xtide-nearest.exe dist/xtide-nearest.exe | |
| cp -f "${{ env.TIDE_EXE }}" dist/tide.exe | |
| cp -f /mingw64/bin/libstdc++-6.dll dist/ | |
| cp -f /mingw64/bin/libgcc_s_seh-1.dll dist/ | |
| cp -f /mingw64/bin/libwinpthread-1.dll dist/ || true | |
| if [ -d "${{ env.TCD_PREFIX }}/bin" ]; then | |
| cp -f "${{ env.TCD_PREFIX }}/bin/"*.dll dist/ || true | |
| fi | |
| [ -f README.md ] && cp -f README.md dist/ || true | |
| [ -f LICENSE ] && cp -f LICENSE dist/ || true | |
| if ldd dist/xtide-nearest.exe | grep -qi "not found"; then | |
| echo "ERROR: xtide-nearest.exe still has missing DLL deps:" | |
| ldd dist/xtide-nearest.exe || true | |
| exit 1 | |
| fi | |
| if ldd dist/tide.exe | grep -qi "not found"; then | |
| echo "ERROR: tide.exe still has missing DLL deps:" | |
| ldd dist/tide.exe || true | |
| exit 1 | |
| fi | |
| ( cd dist && cmake -E tar cfv ../xtide-nearest-${{ env.ARTIFACT_TAG }}-windows-x64.zip --format=zip . ) | |
| - name: Upload artifact | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: windows-x64 | |
| path: xtide-nearest-${{ env.ARTIFACT_TAG }}-windows-x64.zip | |
| publish: | |
| name: publish to GitHub Release (+ flat APT repo) | |
| if: startsWith(github.ref, 'refs/tags/v') | |
| needs: [linux, windows-x64] | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: write | |
| steps: | |
| - name: Download all artifacts | |
| uses: actions/download-artifact@v4 | |
| with: | |
| path: release-assets | |
| - name: Build flat APT repository (Packages/Release) | |
| run: | | |
| set -euo pipefail | |
| sudo apt-get update | |
| sudo apt-get install -y dpkg-dev apt-utils | |
| rm -rf apt-repo | |
| mkdir -p apt-repo | |
| cp -v release-assets/linux-amd64/*.deb apt-repo/ | |
| cp -v release-assets/linux-arm64/*.deb apt-repo/ | |
| pushd apt-repo >/dev/null | |
| dpkg-scanpackages --multiversion . /dev/null > Packages | |
| gzip -9c Packages > Packages.gz | |
| apt-ftparchive release . > Release | |
| popd >/dev/null | |
| - name: Publish versioned release (tag v*) | |
| uses: softprops/action-gh-release@v2 | |
| with: | |
| token: ${{ github.token }} | |
| files: | | |
| release-assets/linux-amd64/*.tar.gz | |
| release-assets/linux-arm64/*.tar.gz | |
| release-assets/windows-x64/*.zip | |
| apt-repo/* | |
| - name: Publish/update stable APT release (tag "apt") | |
| uses: softprops/action-gh-release@v2 | |
| with: | |
| token: ${{ github.token }} | |
| tag_name: apt | |
| name: APT repository (stable) | |
| target_commitish: ${{ github.sha }} | |
| overwrite_files: true | |
| files: | | |
| apt-repo/* |