Compare commits

...

7 commits

Author SHA1 Message Date
copilot-swe-agent[bot] 5513791f75 fix: handle apt showpkg warnings 2026-06-13 16:56:35 -07:00
copilot-swe-agent[bot] fe5b289324 test: add apt warning regression coverage 2026-06-13 16:56:35 -07:00
Mahyar McDonald e58a49f1de add shellcheck linting
- Updated variable expansions to use double quotes for better safety and to prevent word splitting.
- Replaced `ls` with `find` in cache operations to handle non-alphanumeric filenames more robustly.
- Enhanced array handling in scripts by using `${*:N}` syntax for concatenation.
- Improved readability and consistency in conditional checks by using `[[ ... ]]` instead of `[ ... ]`.
- Added comments for clarity on specific operations and shellcheck directives.
2026-06-13 16:54:54 -07:00
Copilot 599df6ec23
Fix apt-cache package lookup failure in nektos/act environments (#172)
* Initial plan

* Add apt package list update to fix nektos/act compatibility

Co-authored-by: awalsh128 <2087466+awalsh128@users.noreply.github.com>

* Remove build artifacts and update .gitignore

Co-authored-by: awalsh128 <2087466+awalsh128@users.noreply.github.com>

* Refactor apt list update logic into shared library function

Co-authored-by: awalsh128 <2087466+awalsh128@users.noreply.github.com>

* Only update apt lists when running in nektos/act environment

Co-authored-by: awalsh128 <2087466+awalsh128@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: awalsh128 <2087466+awalsh128@users.noreply.github.com>
Co-authored-by: Andrew Walsh <awalsh128@gmail.com>
2026-06-13 16:49:12 -07:00
Copilot cd5ee21d96
[WIP] Fix test workflows for actions (#184)
* Initial plan

* Fix test workflows to download artifacts and use local action

- Add needs: [build_binaries] to all test jobs that use the action
- Replace remote action references with local (uses: ./)
- Add checkout step where missing
- Add artifact download and distribute directory setup before action usage
- This ensures binaries built by build_binaries job are available when tests run

Co-authored-by: awalsh128 <2087466+awalsh128@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: awalsh128 <2087466+awalsh128@users.noreply.github.com>
2026-06-13 16:46:15 -07:00
George Joseph c0e0741be6
install_and_cache_pkgs.sh: Fix two issues saving preinst and postinst scripts. (#195)
1.  The logic for listing the files to be cached was using `&&`s between
the listing of the package files, the preinst script and postinst script.
Therefore if there wasn't a preinst script, a postinst script wasn't
checked for.

2. get_install_script_filepath was being passed an empty root file path
so it would never find any files.

Resolves: #194
2026-06-13 16:24:40 -07:00
Copilot 497648d2e1
Apply PR #193 dependency pinning to action.yml (overwrite current action refs) (#201)
* Bump actions/cache off of Node 20

* Bump actions/upload-artifact off of Node 20

---------

Co-authored-by: Sebastian P <241632094+immoseb@users.noreply.github.com>
2026-06-13 16:16:10 -07:00
15 changed files with 1433 additions and 72 deletions

984
.github/workflows/action-tests.yml vendored Normal file
View file

@ -0,0 +1,984 @@
name: Action Tests
on:
workflow_dispatch:
inputs:
debug:
description: "Run in debug mode."
type: boolean
required: false
default: true
repository_dispatch:
push:
pull_request:
env:
DEBUG: ${{ github.event.inputs.debug || false }}
# Test for overrides in built in shell options (regression issue 98).
SHELLOPTS: errexit:pipefail
jobs:
build_binaries:
uses: ./.github/workflows/build-distribute.yml
# All other jobs should depend on build_binaries (if run) or check_distribute (if not needed)
list_all_versions:
needs: [build_binaries]
runs-on: ubuntu-latest
name: List all package versions (including deps).
steps:
- uses: actions/checkout@v3.1.0
- name: Download build artifacts
uses: actions/download-artifact@v4
with:
pattern: cache-apt-pkgs-*
path: distribute-artifacts
- name: Setup distribute directory
shell: bash
run: |
mkdir -p distribute/X64 distribute/X86 distribute/ARM64 distribute/ARM
for artifact_dir in distribute-artifacts/cache-apt-pkgs-*; do
if [ -d "$artifact_dir" ]; then
arch=$(basename "$artifact_dir" | sed 's/cache-apt-pkgs-\([^-]*\)-.*/\1/')
echo "Copying artifacts for architecture: $arch"
cp -r "$artifact_dir"/* "distribute/$arch/" 2>/dev/null || true
fi
done
ls -la distribute/*/ || true
- name: Execute
id: execute
uses: ./
with:
packages: xdot=1.3-1
version: ${{ github.run_id }}-${{ github.run_attempt }}-list_all_versions
debug: ${{ env.DEBUG }}
- name: Verify
if: |
steps.execute.outputs.cache-hit != 'false' ||
steps.execute.outputs.all-package-version-list != 'fonts-liberation2=1:2.1.5-3,gir1.2-atk-1.0=2.52.0-1build1,gir1.2-freedesktop=1.80.1-1,gir1.2-gdkpixbuf-2.0=2.42.10+dfsg-3ubuntu3.2,gir1.2-gtk-3.0=3.24.41-4ubuntu1.3,gir1.2-harfbuzz-0.0=8.3.0-2build2,gir1.2-pango-1.0=1.52.1+ds-1build1,graphviz=2.42.2-9ubuntu0.1,libann0=1.1.2+doc-9build1,libblas3=3.12.0-3build1.1,libcdt5=2.42.2-9ubuntu0.1,libcgraph6=2.42.2-9ubuntu0.1,libgts-0.7-5t64=0.7.6+darcs121130-5.2build1,libgts-bin=0.7.6+darcs121130-5.2build1,libgvc6=2.42.2-9ubuntu0.1,libgvpr2=2.42.2-9ubuntu0.1,libharfbuzz-gobject0=8.3.0-2build2,liblab-gamut1=2.42.2-9ubuntu0.1,liblapack3=3.12.0-3build1.1,libpangoxft-1.0-0=1.52.1+ds-1build1,libpathplan4=2.42.2-9ubuntu0.1,python3-cairo=1.25.1-2build2,python3-gi-cairo=3.48.2-1,python3-numpy=1:1.26.4+ds-6ubuntu1,xdot=1.3-1'
run: |
echo "cache-hit = ${{ steps.execute.outputs.cache-hit }}"
echo "package-version-list = ${{ steps.execute.outputs.package-version-list }}"
echo "all-package-version-list = ${{ steps.execute.outputs.all-package-version-list }}"
echo "diff all-package-version-list"
diff <(echo "${{ steps.execute.outputs.all-package-version-list }}" ) <(echo "fonts-liberation2=1:2.1.5-3,gir1.2-atk-1.0=2.52.0-1build1,gir1.2-freedesktop=1.80.1-1,gir1.2-gdkpixbuf-2.0=2.42.10+dfsg-3ubuntu3.2,gir1.2-gtk-3.0=3.24.41-4ubuntu1.3,gir1.2-harfbuzz-0.0=8.3.0-2build2,gir1.2-pango-1.0=1.52.1+ds-1build1,graphviz=2.42.2-9ubuntu0.1,libann0=1.1.2+doc-9build1,libblas3=3.12.0-3build1.1,libcdt5=2.42.2-9ubuntu0.1,libcgraph6=2.42.2-9ubuntu0.1,libgts-0.7-5t64=0.7.6+darcs121130-5.2build1,libgts-bin=0.7.6+darcs121130-5.2build1,libgvc6=2.42.2-9ubuntu0.1,libgvpr2=2.42.2-9ubuntu0.1,libharfbuzz-gobject0=8.3.0-2build2,liblab-gamut1=2.42.2-9ubuntu0.1,liblapack3=3.12.0-3build1.1,libpangoxft-1.0-0=1.52.1+ds-1build1,libpathplan4=2.42.2-9ubuntu0.1,python3-cairo=1.25.1-2build2,python3-gi-cairo=3.48.2-1,python3-numpy=1:1.26.4+ds-6ubuntu1,xdot=1.3-1")
exit 1
shell: bash
list_versions:
needs: [build_binaries]
runs-on: ubuntu-latest
name: List package versions.
steps:
- uses: actions/checkout@v3.1.0
- name: Download build artifacts
uses: actions/download-artifact@v4
with:
pattern: cache-apt-pkgs-*
path: distribute-artifacts
- name: Setup distribute directory
shell: bash
run: |
mkdir -p distribute/X64 distribute/X86 distribute/ARM64 distribute/ARM
for artifact_dir in distribute-artifacts/cache-apt-pkgs-*; do
if [ -d "$artifact_dir" ]; then
arch=$(basename "$artifact_dir" | sed 's/cache-apt-pkgs-\([^-]*\)-.*/\1/')
echo "Copying artifacts for architecture: $arch"
cp -r "$artifact_dir"/* "distribute/$arch/" 2>/dev/null || true
fi
done
ls -la distribute/*/ || true
- name: Execute
id: execute
uses: ./
with:
packages: xdot rolldice
version: ${{ github.run_id }}-${{ github.run_attempt }}-list_versions
debug: ${{ env.DEBUG }}
- name: Verify
if:
steps.execute.outputs.cache-hit != 'false' || steps.execute.outputs.package-version-list
!= 'rolldice=1.16-1build3,xdot=1.3-1'
run: |
echo "cache-hit = ${{ steps.execute.outputs.cache-hit }}"
echo "package-version-list = ${{ steps.execute.outputs.package-version-list }}"
echo "diff package-version-list"
diff <(echo "${{ steps.execute.outputs.package-version-list }}" ) <(echo "rolldice=1.16-1build3,xdot=1.3-1")
exit 1
shell: bash
standard_workflow_install:
needs: [build_binaries]
runs-on: ubuntu-latest
name: Standard workflow install package and cache.
steps:
- uses: actions/checkout@v3.1.0
- name: Download build artifacts
uses: actions/download-artifact@v4
with:
pattern: cache-apt-pkgs-*
path: distribute-artifacts
- name: Setup distribute directory
shell: bash
run: |
mkdir -p distribute/X64 distribute/X86 distribute/ARM64 distribute/ARM
for artifact_dir in distribute-artifacts/cache-apt-pkgs-*; do
if [ -d "$artifact_dir" ]; then
arch=$(basename "$artifact_dir" | sed 's/cache-apt-pkgs-\([^-]*\)-.*/\1/')
echo "Copying artifacts for architecture: $arch"
cp -r "$artifact_dir"/* "distribute/$arch/" 2>/dev/null || true
fi
done
ls -la distribute/*/ || true
- name: Execute
id: execute
uses: ./
with:
packages: xdot rolldice
version: ${{ github.run_id }}-${{ github.run_attempt }}-standard_workflow
debug: ${{ env.DEBUG }}
- name: Verify
if: steps.execute.outputs.cache-hit != 'false'
run: |
echo "cache-hit = ${{ steps.execute.outputs.cache-hit }}"
exit 1
shell: bash
standard_workflow_install_with_new_version:
needs: [standard_workflow_install, build_binaries]
runs-on: ubuntu-latest
name: Standard workflow packages with new version.
steps:
- uses: actions/checkout@v3.1.0
- name: Download build artifacts
uses: actions/download-artifact@v4
with:
pattern: cache-apt-pkgs-*
path: distribute-artifacts
- name: Setup distribute directory
shell: bash
run: |
mkdir -p distribute/X64 distribute/X86 distribute/ARM64 distribute/ARM
for artifact_dir in distribute-artifacts/cache-apt-pkgs-*; do
if [ -d "$artifact_dir" ]; then
arch=$(basename "$artifact_dir" | sed 's/cache-apt-pkgs-\([^-]*\)-.*/\1/')
echo "Copying artifacts for architecture: $arch"
cp -r "$artifact_dir"/* "distribute/$arch/" 2>/dev/null || true
fi
done
ls -la distribute/*/ || true
- name: Execute
id: execute
uses: ./
with:
packages: xdot rolldice
version:
${{ github.run_id }}-${{ github.run_attempt}}-standard_workflow_install_with_new_version
debug: ${{ env.DEBUG }}
- name: Verify
if: steps.execute.outputs.cache-hit != 'false'
run: |
echo "cache-hit = ${{ steps.execute.outputs.cache-hit }}"
exit 1
shell: bash
standard_workflow_restore:
needs: [standard_workflow_install, build_binaries]
runs-on: ubuntu-latest
name: Standard workflow restore cached packages.
steps:
- uses: actions/checkout@v3.1.0
- name: Download build artifacts
uses: actions/download-artifact@v4
with:
pattern: cache-apt-pkgs-*
path: distribute-artifacts
- name: Setup distribute directory
shell: bash
run: |
mkdir -p distribute/X64 distribute/X86 distribute/ARM64 distribute/ARM
for artifact_dir in distribute-artifacts/cache-apt-pkgs-*; do
if [ -d "$artifact_dir" ]; then
arch=$(basename "$artifact_dir" | sed 's/cache-apt-pkgs-\([^-]*\)-.*/\1/')
echo "Copying artifacts for architecture: $arch"
cp -r "$artifact_dir"/* "distribute/$arch/" 2>/dev/null || true
fi
done
ls -la distribute/*/ || true
- name: Execute
id: execute
uses: ./
with:
packages: xdot rolldice
version: ${{ github.run_id }}-${{ github.run_attempt }}-standard_workflow
debug: ${{ env.DEBUG }}
- name: Verify
if: steps.execute.outputs.cache-hit != 'true'
run: |
echo "cache-hit = ${{ steps.execute.outputs.cache-hit }}"
exit 1
shell: bash
standard_workflow_restore_with_packages_out_of_order:
needs: [standard_workflow_install, build_binaries]
runs-on: ubuntu-latest
name: Standard workflow restore with packages out of order.
steps:
- uses: actions/checkout@v3.1.0
- name: Download build artifacts
uses: actions/download-artifact@v4
with:
pattern: cache-apt-pkgs-*
path: distribute-artifacts
- name: Setup distribute directory
shell: bash
run: |
mkdir -p distribute/X64 distribute/X86 distribute/ARM64 distribute/ARM
for artifact_dir in distribute-artifacts/cache-apt-pkgs-*; do
if [ -d "$artifact_dir" ]; then
arch=$(basename "$artifact_dir" | sed 's/cache-apt-pkgs-\([^-]*\)-.*/\1/')
echo "Copying artifacts for architecture: $arch"
cp -r "$artifact_dir"/* "distribute/$arch/" 2>/dev/null || true
fi
done
ls -la distribute/*/ || true
- name: Execute
id: execute
uses: ./
with:
packages: rolldice xdot
version: ${{ github.run_id }}-${{ github.run_attempt }}-standard_workflow
debug: ${{ env.DEBUG }}
- name: Verify
if: steps.execute.outputs.cache-hit != 'true'
run: |
echo "cache-hit = ${{ steps.execute.outputs.cache-hit }}"
exit 1
shell: bash
standard_workflow_add_package:
needs: [standard_workflow_install, build_binaries]
runs-on: ubuntu-latest
name: Standard workflow add another package.
steps:
- uses: actions/checkout@v3.1.0
- name: Download build artifacts
uses: actions/download-artifact@v4
with:
pattern: cache-apt-pkgs-*
path: distribute-artifacts
- name: Setup distribute directory
shell: bash
run: |
mkdir -p distribute/X64 distribute/X86 distribute/ARM64 distribute/ARM
for artifact_dir in distribute-artifacts/cache-apt-pkgs-*; do
if [ -d "$artifact_dir" ]; then
arch=$(basename "$artifact_dir" | sed 's/cache-apt-pkgs-\([^-]*\)-.*/\1/')
echo "Copying artifacts for architecture: $arch"
cp -r "$artifact_dir"/* "distribute/$arch/" 2>/dev/null || true
fi
done
ls -la distribute/*/ || true
- name: Execute
id: execute
uses: ./
with:
packages: xdot rolldice distro-info-data
version: ${{ github.run_id }}-${{ github.run_attempt }}-standard_workflow
debug: ${{ env.DEBUG }}
- name: Verify
if: steps.execute.outputs.cache-hit != 'false'
run: |
echo "cache-hit = ${{ steps.execute.outputs.cache-hit }}"
exit 1
shell: bash
standard_workflow_restore_add_package:
needs: [standard_workflow_add_package, build_binaries]
runs-on: ubuntu-latest
name: Standard workflow restore added package.
steps:
- uses: actions/checkout@v3.1.0
- name: Download build artifacts
uses: actions/download-artifact@v4
with:
pattern: cache-apt-pkgs-*
path: distribute-artifacts
- name: Setup distribute directory
shell: bash
run: |
mkdir -p distribute/X64 distribute/X86 distribute/ARM64 distribute/ARM
for artifact_dir in distribute-artifacts/cache-apt-pkgs-*; do
if [ -d "$artifact_dir" ]; then
arch=$(basename "$artifact_dir" | sed 's/cache-apt-pkgs-\([^-]*\)-.*/\1/')
echo "Copying artifacts for architecture: $arch"
cp -r "$artifact_dir"/* "distribute/$arch/" 2>/dev/null || true
fi
done
ls -la distribute/*/ || true
- name: Execute
id: execute
uses: ./
with:
packages: xdot rolldice distro-info-data
version: ${{ github.run_id }}-${{ github.run_attempt }}-standard_workflow
debug: ${{ env.DEBUG }}
- name: Verify
if: steps.execute.outputs.cache-hit != 'true'
run: |
echo "cache-hit = ${{ steps.execute.outputs.cache-hit }}"
exit 1
shell: bash
no_packages:
needs: [build_binaries]
runs-on: ubuntu-latest
name: No packages passed.
steps:
- uses: actions/checkout@v3.1.0
- name: Download build artifacts
uses: actions/download-artifact@v4
with:
pattern: cache-apt-pkgs-*
path: distribute-artifacts
- name: Setup distribute directory
shell: bash
run: |
mkdir -p distribute/X64 distribute/X86 distribute/ARM64 distribute/ARM
for artifact_dir in distribute-artifacts/cache-apt-pkgs-*; do
if [ -d "$artifact_dir" ]; then
arch=$(basename "$artifact_dir" | sed 's/cache-apt-pkgs-\([^-]*\)-.*/\1/')
echo "Copying artifacts for architecture: $arch"
cp -r "$artifact_dir"/* "distribute/$arch/" 2>/dev/null || true
fi
done
ls -la distribute/*/ || true
- name: Execute
id: execute
uses: ./
with:
packages: ""
continue-on-error: true
- name: Verify
if: steps.execute.outcome == 'failure'
run: exit 0
shell: bash
package_not_found:
needs: [build_binaries]
runs-on: ubuntu-latest
name: Package not found.
steps:
- uses: actions/checkout@v3.1.0
- name: Download build artifacts
uses: actions/download-artifact@v4
with:
pattern: cache-apt-pkgs-*
path: distribute-artifacts
- name: Setup distribute directory
shell: bash
run: |
mkdir -p distribute/X64 distribute/X86 distribute/ARM64 distribute/ARM
for artifact_dir in distribute-artifacts/cache-apt-pkgs-*; do
if [ -d "$artifact_dir" ]; then
arch=$(basename "$artifact_dir" | sed 's/cache-apt-pkgs-\([^-]*\)-.*/\1/')
echo "Copying artifacts for architecture: $arch"
cp -r "$artifact_dir"/* "distribute/$arch/" 2>/dev/null || true
fi
done
ls -la distribute/*/ || true
- name: Execute
id: execute
uses: ./
with:
packages: package_that_doesnt_exist
continue-on-error: true
- name: Verify
if: steps.execute.outcome == 'failure'
run: exit 0
shell: bash
version_contains_spaces:
needs: [build_binaries]
runs-on: ubuntu-latest
name: Version contains spaces.
steps:
- uses: actions/checkout@v3.1.0
- name: Download build artifacts
uses: actions/download-artifact@v4
with:
pattern: cache-apt-pkgs-*
path: distribute-artifacts
- name: Setup distribute directory
shell: bash
run: |
mkdir -p distribute/X64 distribute/X86 distribute/ARM64 distribute/ARM
for artifact_dir in distribute-artifacts/cache-apt-pkgs-*; do
if [ -d "$artifact_dir" ]; then
arch=$(basename "$artifact_dir" | sed 's/cache-apt-pkgs-\([^-]*\)-.*/\1/')
echo "Copying artifacts for architecture: $arch"
cp -r "$artifact_dir"/* "distribute/$arch/" 2>/dev/null || true
fi
done
ls -la distribute/*/ || true
- name: Execute
id: execute
uses: ./
with:
packages: xdot
version: 123 abc
debug: ${{ env.DEBUG }}
continue-on-error: true
- name: Verify
if: steps.execute.outcome == 'failure'
run: exit 0
shell: bash
regression_36:
needs: [build_binaries]
runs-on: ubuntu-latest
name: "Reinstall existing package (regression issue #36)."
steps:
- uses: actions/checkout@v3.1.0
- name: Download build artifacts
uses: actions/download-artifact@v4
with:
pattern: cache-apt-pkgs-*
path: distribute-artifacts
- name: Setup distribute directory
shell: bash
run: |
mkdir -p distribute/X64 distribute/X86 distribute/ARM64 distribute/ARM
for artifact_dir in distribute-artifacts/cache-apt-pkgs-*; do
if [ -d "$artifact_dir" ]; then
arch=$(basename "$artifact_dir" | sed 's/cache-apt-pkgs-\([^-]*\)-.*/\1/')
echo "Copying artifacts for architecture: $arch"
cp -r "$artifact_dir"/* "distribute/$arch/" 2>/dev/null || true
fi
done
ls -la distribute/*/ || true
- uses: ./
with:
packages: libgtk-3-dev
version: ${{ github.run_id }}-${{ github.run_attempt }}-regression_36
debug: ${{ env.DEBUG }}
regression_37:
needs: [build_binaries]
runs-on: ubuntu-latest
name: "Install with reported package dependencies not installed (regression issue #37)."
steps:
- uses: actions/checkout@v3.1.0
- name: Download build artifacts
uses: actions/download-artifact@v4
with:
pattern: cache-apt-pkgs-*
path: distribute-artifacts
- name: Setup distribute directory
shell: bash
run: |
mkdir -p distribute/X64 distribute/X86 distribute/ARM64 distribute/ARM
for artifact_dir in distribute-artifacts/cache-apt-pkgs-*; do
if [ -d "$artifact_dir" ]; then
arch=$(basename "$artifact_dir" | sed 's/cache-apt-pkgs-\([^-]*\)-.*/\1/')
echo "Copying artifacts for architecture: $arch"
cp -r "$artifact_dir"/* "distribute/$arch/" 2>/dev/null || true
fi
done
ls -la distribute/*/ || true
- uses: ./
with:
packages: libosmesa6-dev libgl1-mesa-dev python3-tk pandoc git-restore-mtime
version: ${{ github.run_id }}-${{ github.run_attempt }}-regression_37
debug: ${{ env.DEBUG }}
debug_disabled:
needs: [build_binaries]
runs-on: ubuntu-latest
name: Debug disabled.
steps:
- uses: actions/checkout@v3.1.0
- name: Download build artifacts
uses: actions/download-artifact@v4
with:
pattern: cache-apt-pkgs-*
path: distribute-artifacts
- name: Setup distribute directory
shell: bash
run: |
mkdir -p distribute/X64 distribute/X86 distribute/ARM64 distribute/ARM
for artifact_dir in distribute-artifacts/cache-apt-pkgs-*; do
if [ -d "$artifact_dir" ]; then
arch=$(basename "$artifact_dir" | sed 's/cache-apt-pkgs-\([^-]*\)-.*/\1/')
echo "Copying artifacts for architecture: $arch"
cp -r "$artifact_dir"/* "distribute/$arch/" 2>/dev/null || true
fi
done
ls -la distribute/*/ || true
- uses: ./
with:
packages: xdot
version: ${{ github.run_id }}-${{ github.run_attempt }}-list-all-package-versions
debug: ${{ env.DEBUG }}
regression_72_1:
needs: [build_binaries]
runs-on: ubuntu-latest
name: "Cache Java CA certs package v1 (regression issue #72)."
steps:
- uses: actions/checkout@v3.1.0
- name: Download build artifacts
uses: actions/download-artifact@v4
with:
pattern: cache-apt-pkgs-*
path: distribute-artifacts
- name: Setup distribute directory
shell: bash
run: |
mkdir -p distribute/X64 distribute/X86 distribute/ARM64 distribute/ARM
for artifact_dir in distribute-artifacts/cache-apt-pkgs-*; do
if [ -d "$artifact_dir" ]; then
arch=$(basename "$artifact_dir" | sed 's/cache-apt-pkgs-\([^-]*\)-.*/\1/')
echo "Copying artifacts for architecture: $arch"
cp -r "$artifact_dir"/* "distribute/$arch/" 2>/dev/null || true
fi
done
ls -la distribute/*/ || true
- uses: ./
with:
packages: openjdk-11-jre
version: ${{ github.run_id }}-${{ github.run_attempt }}-regression_72
debug: ${{ env.DEBUG }}
regression_72_2:
needs: [build_binaries]
runs-on: ubuntu-latest
name: "Cache Java CA certs package v2 (regression issue #72)."
steps:
- uses: actions/checkout@v3.1.0
- name: Download build artifacts
uses: actions/download-artifact@v4
with:
pattern: cache-apt-pkgs-*
path: distribute-artifacts
- name: Setup distribute directory
shell: bash
run: |
mkdir -p distribute/X64 distribute/X86 distribute/ARM64 distribute/ARM
for artifact_dir in distribute-artifacts/cache-apt-pkgs-*; do
if [ -d "$artifact_dir" ]; then
arch=$(basename "$artifact_dir" | sed 's/cache-apt-pkgs-\([^-]*\)-.*/\1/')
echo "Copying artifacts for architecture: $arch"
cp -r "$artifact_dir"/* "distribute/$arch/" 2>/dev/null || true
fi
done
ls -la distribute/*/ || true
- uses: ./
with:
packages: default-jre
version: ${{ github.run_id }}-${{ github.run_attempt }}-regression_72
debug: ${{ env.DEBUG }}
regression_76:
needs: [build_binaries]
runs-on: ubuntu-latest
name: "Cache empty archive (regression issue #76)."
steps:
- uses: actions/checkout@v3.1.0
- name: Download build artifacts
uses: actions/download-artifact@v4
with:
pattern: cache-apt-pkgs-*
path: distribute-artifacts
- name: Setup distribute directory
shell: bash
run: |
mkdir -p distribute/X64 distribute/X86 distribute/ARM64 distribute/ARM
for artifact_dir in distribute-artifacts/cache-apt-pkgs-*; do
if [ -d "$artifact_dir" ]; then
arch=$(basename "$artifact_dir" | sed 's/cache-apt-pkgs-\([^-]*\)-.*/\1/')
echo "Copying artifacts for architecture: $arch"
cp -r "$artifact_dir"/* "distribute/$arch/" 2>/dev/null || true
fi
done
ls -la distribute/*/ || true
- run: |
sudo wget -O- https://apt.repos.intel.com/intel-gpg-keys/GPG-PUB-KEY-INTEL-SW-PRODUCTS.PUB | gpg --dearmor | tee /usr/share/keyrings/oneapi-archive-keyring.gpg > /dev/null;
echo "deb [signed-by=/usr/share/keyrings/oneapi-archive-keyring.gpg] https://apt.repos.intel.com/oneapi all main" | sudo tee /etc/apt/sources.list.d/oneAPI.list;
sudo apt-get -qq update;
sudo apt-get install -y intel-oneapi-runtime-libs intel-oneapi-runtime-opencl;
sudo apt-get install -y opencl-headers ocl-icd-opencl-dev;
sudo apt-get install -y libsundials-dev;
- uses: ./
with:
packages: intel-oneapi-runtime-libs
version: ${{ github.run_id }}-${{ github.run_attempt }}-regression_76
debug: ${{ env.DEBUG }}
regression_79:
needs: [build_binaries]
runs-on: ubuntu-latest
name: "Tar error with libboost-dev (regression issue #79)."
steps:
- uses: actions/checkout@v3.1.0
- name: Download build artifacts
uses: actions/download-artifact@v4
with:
pattern: cache-apt-pkgs-*
path: distribute-artifacts
- name: Setup distribute directory
shell: bash
run: |
mkdir -p distribute/X64 distribute/X86 distribute/ARM64 distribute/ARM
for artifact_dir in distribute-artifacts/cache-apt-pkgs-*; do
if [ -d "$artifact_dir" ]; then
arch=$(basename "$artifact_dir" | sed 's/cache-apt-pkgs-\([^-]*\)-.*/\1/')
echo "Copying artifacts for architecture: $arch"
cp -r "$artifact_dir"/* "distribute/$arch/" 2>/dev/null || true
fi
done
ls -la distribute/*/ || true
- uses: ./
with:
packages: libboost-dev
version: ${{ github.run_id }}-${{ github.run_attempt }}-regression_79
debug: ${{ env.DEBUG }}
regression_81:
needs: [build_binaries]
runs-on: ubuntu-latest
name: "Tar error with alsa-ucm-conf (regression issue #81)."
steps:
- uses: actions/checkout@v3.1.0
- name: Download build artifacts
uses: actions/download-artifact@v4
with:
pattern: cache-apt-pkgs-*
path: distribute-artifacts
- name: Setup distribute directory
shell: bash
run: |
mkdir -p distribute/X64 distribute/X86 distribute/ARM64 distribute/ARM
for artifact_dir in distribute-artifacts/cache-apt-pkgs-*; do
if [ -d "$artifact_dir" ]; then
arch=$(basename "$artifact_dir" | sed 's/cache-apt-pkgs-\([^-]*\)-.*/\1/')
echo "Copying artifacts for architecture: $arch"
cp -r "$artifact_dir"/* "distribute/$arch/" 2>/dev/null || true
fi
done
ls -la distribute/*/ || true
- uses: ./
with:
packages:
libasound2 libatk-bridge2.0-0 libatk1.0-0 libatspi2.0-0 libcups2 libdrm2 libgbm1
libnspr4 libnss3 libxcomposite1 libxdamage1 libxfixes3 libxkbcommon0 libxrandr2
version: ${{ github.run_id }}-${{ github.run_attempt }}-regression_81
debug: ${{ env.DEBUG }}
regression_84_literal_block_install:
needs: [build_binaries]
runs-on: ubuntu-latest
name: "Install multiline package listing using literal block style (regression issue #84)."
steps:
- uses: actions/checkout@v3.1.0
- name: Download build artifacts
uses: actions/download-artifact@v4
with:
pattern: cache-apt-pkgs-*
path: distribute-artifacts
- name: Setup distribute directory
shell: bash
run: |
mkdir -p distribute/X64 distribute/X86 distribute/ARM64 distribute/ARM
for artifact_dir in distribute-artifacts/cache-apt-pkgs-*; do
if [ -d "$artifact_dir" ]; then
arch=$(basename "$artifact_dir" | sed 's/cache-apt-pkgs-\([^-]*\)-.*/\1/')
echo "Copying artifacts for architecture: $arch"
cp -r "$artifact_dir"/* "distribute/$arch/" 2>/dev/null || true
fi
done
ls -la distribute/*/ || true
- uses: ./
with:
packages: >
xdot rolldice distro-info-data
version: ${{ github.run_id }}-${{ github.run_attempt }}-regression_84_literal_block
debug: ${{ env.DEBUG }}
regression_84_literal_block_restore:
needs: [regression_84_literal_block_install, build_binaries]
runs-on: ubuntu-latest
name: "Restore multiline package listing using literal block style (regression issue #84)."
steps:
- uses: actions/checkout@v3.1.0
- name: Download build artifacts
uses: actions/download-artifact@v4
with:
pattern: cache-apt-pkgs-*
path: distribute-artifacts
- name: Setup distribute directory
shell: bash
run: |
mkdir -p distribute/X64 distribute/X86 distribute/ARM64 distribute/ARM
for artifact_dir in distribute-artifacts/cache-apt-pkgs-*; do
if [ -d "$artifact_dir" ]; then
arch=$(basename "$artifact_dir" | sed 's/cache-apt-pkgs-\([^-]*\)-.*/\1/')
echo "Copying artifacts for architecture: $arch"
cp -r "$artifact_dir"/* "distribute/$arch/" 2>/dev/null || true
fi
done
ls -la distribute/*/ || true
- name: Execute
id: execute
uses: ./
with:
packages: xdot rolldice distro-info-data
version: ${{ github.run_id }}-${{ github.run_attempt }}-regression_84_literal_block
debug: ${{ env.DEBUG }}
- name: Verify
if: steps.execute.outputs.cache-hit != 'true'
run: |
echo "cache-hit = ${{ steps.execute.outputs.cache-hit }}"
exit 1
shell: bash
regression_84_folded_block_install:
needs: [build_binaries]
runs-on: ubuntu-latest
name: "Install multiline package listing using literal block style (regression issue #84)."
steps:
- uses: actions/checkout@v3.1.0
- name: Download build artifacts
uses: actions/download-artifact@v4
with:
pattern: cache-apt-pkgs-*
path: distribute-artifacts
- name: Setup distribute directory
shell: bash
run: |
mkdir -p distribute/X64 distribute/X86 distribute/ARM64 distribute/ARM
for artifact_dir in distribute-artifacts/cache-apt-pkgs-*; do
if [ -d "$artifact_dir" ]; then
arch=$(basename "$artifact_dir" | sed 's/cache-apt-pkgs-\([^-]*\)-.*/\1/')
echo "Copying artifacts for architecture: $arch"
cp -r "$artifact_dir"/* "distribute/$arch/" 2>/dev/null || true
fi
done
ls -la distribute/*/ || true
- uses: ./
with:
packages: |
xdot \
rolldice distro-info-data
version: ${{ github.run_id }}-${{ github.run_attempt }}-regression_84_folded_block
debug: ${{ env.DEBUG }}
regression_84_folded_block_restore:
needs: [regression_84_folded_block_install, build_binaries]
runs-on: ubuntu-latest
name: "Restore multiline package listing using literal block style (regression issue #84)."
steps:
- uses: actions/checkout@v3.1.0
- name: Download build artifacts
uses: actions/download-artifact@v4
with:
pattern: cache-apt-pkgs-*
path: distribute-artifacts
- name: Setup distribute directory
shell: bash
run: |
mkdir -p distribute/X64 distribute/X86 distribute/ARM64 distribute/ARM
for artifact_dir in distribute-artifacts/cache-apt-pkgs-*; do
if [ -d "$artifact_dir" ]; then
arch=$(basename "$artifact_dir" | sed 's/cache-apt-pkgs-\([^-]*\)-.*/\1/')
echo "Copying artifacts for architecture: $arch"
cp -r "$artifact_dir"/* "distribute/$arch/" 2>/dev/null || true
fi
done
ls -la distribute/*/ || true
- name: Execute
id: execute
uses: ./
with:
packages: xdot rolldice distro-info-data
version: ${{ github.run_id }}-${{ github.run_attempt }}-regression_84_folded_block
debug: ${{ env.DEBUG }}
- name: Verify
if: steps.execute.outputs.cache-hit != 'true'
run: |
echo "cache-hit = ${{ steps.execute.outputs.cache-hit }}"
exit 1
shell: bash
regression_89:
needs: [build_binaries]
runs-on: ubuntu-latest
name: "Upload logs artifact name (regression issue #89)."
steps:
- uses: actions/checkout@v3.1.0
- name: Download build artifacts
uses: actions/download-artifact@v4
with:
pattern: cache-apt-pkgs-*
path: distribute-artifacts
- name: Setup distribute directory
shell: bash
run: |
mkdir -p distribute/X64 distribute/X86 distribute/ARM64 distribute/ARM
for artifact_dir in distribute-artifacts/cache-apt-pkgs-*; do
if [ -d "$artifact_dir" ]; then
arch=$(basename "$artifact_dir" | sed 's/cache-apt-pkgs-\([^-]*\)-.*/\1/')
echo "Copying artifacts for architecture: $arch"
cp -r "$artifact_dir"/* "distribute/$arch/" 2>/dev/null || true
fi
done
ls -la distribute/*/ || true
- uses: ./
with:
packages: libgtk-3-dev:amd64
version: ${{ github.run_id }}-${{ github.run_attempt }}-regression_89
debug: ${{ env.DEBUG }}
regression_98:
needs: [build_binaries]
runs-on: ubuntu-latest
name: "Install error due to SHELLOPTS override (regression issue #98)."
steps:
- uses: actions/checkout@v3.1.0
- name: Download build artifacts
uses: actions/download-artifact@v4
with:
pattern: cache-apt-pkgs-*
path: distribute-artifacts
- name: Setup distribute directory
shell: bash
run: |
mkdir -p distribute/X64 distribute/X86 distribute/ARM64 distribute/ARM
for artifact_dir in distribute-artifacts/cache-apt-pkgs-*; do
if [ -d "$artifact_dir" ]; then
arch=$(basename "$artifact_dir" | sed 's/cache-apt-pkgs-\([^-]*\)-.*/\1/')
echo "Copying artifacts for architecture: $arch"
cp -r "$artifact_dir"/* "distribute/$arch/" 2>/dev/null || true
fi
done
ls -la distribute/*/ || true
- uses: ./
with:
packages: git-restore-mtime libgl1-mesa-dev libosmesa6-dev pandoc
version: ${{ github.run_id }}-${{ github.run_attempt }}-regression_98
debug: ${{ env.DEBUG }}
regression_106_install:
needs: [build_binaries]
runs-on: ubuntu-latest
name: "Stale apt repo not finding package on restore, install phase (regression issue #106)."
steps:
- uses: actions/checkout@v3.1.0
- name: Download build artifacts
uses: actions/download-artifact@v4
with:
pattern: cache-apt-pkgs-*
path: distribute-artifacts
- name: Setup distribute directory
shell: bash
run: |
mkdir -p distribute/X64 distribute/X86 distribute/ARM64 distribute/ARM
for artifact_dir in distribute-artifacts/cache-apt-pkgs-*; do
if [ -d "$artifact_dir" ]; then
arch=$(basename "$artifact_dir" | sed 's/cache-apt-pkgs-\([^-]*\)-.*/\1/')
echo "Copying artifacts for architecture: $arch"
cp -r "$artifact_dir"/* "distribute/$arch/" 2>/dev/null || true
fi
done
ls -la distribute/*/ || true
- uses: ./
with:
packages: libtk8.6
version: ${{ github.run_id }}-${{ github.run_attempt }}-regression_106
debug: ${{ env.DEBUG }}
regression_106_restore:
needs: [regression_106_install, build_binaries]
runs-on: ubuntu-latest
name: "Stale apt repo not finding package on restore, restore phase (regression issue #106)."
steps:
- uses: actions/checkout@v3.1.0
- name: Download build artifacts
uses: actions/download-artifact@v4
with:
pattern: cache-apt-pkgs-*
path: distribute-artifacts
- name: Setup distribute directory
shell: bash
run: |
mkdir -p distribute/X64 distribute/X86 distribute/ARM64 distribute/ARM
for artifact_dir in distribute-artifacts/cache-apt-pkgs-*; do
if [ -d "$artifact_dir" ]; then
arch=$(basename "$artifact_dir" | sed 's/cache-apt-pkgs-\([^-]*\)-.*/\1/')
echo "Copying artifacts for architecture: $arch"
cp -r "$artifact_dir"/* "distribute/$arch/" 2>/dev/null || true
fi
done
ls -la distribute/*/ || true
- uses: ./
with:
packages: libtk8.6
version: ${{ github.run_id }}-${{ github.run_attempt }}-regression_106
debug: ${{ env.DEBUG }}
multi_arch_cache_key:
needs: [build_binaries]
runs-on: ubuntu-latest
name: "Cache packages with multi-arch cache key."
steps:
- uses: actions/checkout@v3.1.0
- name: Download build artifacts
uses: actions/download-artifact@v4
with:
pattern: cache-apt-pkgs-*
path: distribute-artifacts
- name: Setup distribute directory
shell: bash
run: |
mkdir -p distribute/X64 distribute/X86 distribute/ARM64 distribute/ARM
for artifact_dir in distribute-artifacts/cache-apt-pkgs-*; do
if [ -d "$artifact_dir" ]; then
arch=$(basename "$artifact_dir" | sed 's/cache-apt-pkgs-\([^-]*\)-.*/\1/')
echo "Copying artifacts for architecture: $arch"
cp -r "$artifact_dir"/* "distribute/$arch/" 2>/dev/null || true
fi
done
ls -la distribute/*/ || true
- uses: ./
with:
packages: libfuse2
version: ${{ github.run_id }}-${{ github.run_attempt }}-multi_arch_cache_key
debug: ${{ env.DEBUG }}
virtual_package:
needs: [build_binaries]
runs-on: ubuntu-latest
name: "Cache virtual package."
steps:
- uses: actions/checkout@v3.1.0
- name: Download build artifacts
uses: actions/download-artifact@v4
with:
pattern: cache-apt-pkgs-*
path: distribute-artifacts
- name: Setup distribute directory
shell: bash
run: |
mkdir -p distribute/X64 distribute/X86 distribute/ARM64 distribute/ARM
for artifact_dir in distribute-artifacts/cache-apt-pkgs-*; do
if [ -d "$artifact_dir" ]; then
arch=$(basename "$artifact_dir" | sed 's/cache-apt-pkgs-\([^-]*\)-.*/\1/')
echo "Copying artifacts for architecture: $arch"
cp -r "$artifact_dir"/* "distribute/$arch/" 2>/dev/null || true
fi
done
ls -la distribute/*/ || true
- uses: ./
with:
packages: libvips
version: ${{ github.run_id }}-${{ github.run_attempt }}-virtual_package
debug: ${{ env.DEBUG }}

118
.github/workflows/build-distribute.yml vendored Normal file
View file

@ -0,0 +1,118 @@
name: Build and Release Distribute Artifacts
on:
push:
tags:
- "v2.*.*"
branches:
- dev-v2
workflow_call:
permissions:
contents: write
id-token: write
env:
VERSION_PREFIX: "commit"
working_directory: ${{ github.workspace }}
jobs:
build-and-release:
runs-on: ubuntu-latest
strategy:
matrix:
include:
- goos: linux
goarch: amd64
arch: X64
- goos: linux
goarch: arm64
arch: ARM64
- goos: linux
goarch: arm
goarch_variant: "6"
arch: ARM
- goos: linux
goarch: "386"
arch: X86
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Set up Go
uses: actions/setup-go@v4
with:
go-version: 1.24
- name: Generate version from commit SHA
id: version
run: ./scripts/distribute.sh generate-version
- name: Create distribute directory
run: ./scripts/distribute.sh create-distribute-directory "${{ matrix.arch }}"
- name: Clone apt-fast repository
run: ./scripts/distribute.sh clone-apt-fast
- name: Build binary for ${{ matrix.goos }}/${{ matrix.goarch }}
env:
GOOS: ${{ matrix.goos }}
GOARCH: ${{ matrix.goarch }}
GOARM: ${{ matrix.goarch_variant }}
CGO_ENABLED: 0
run: |
./scripts/distribute.sh build-binary \
"${{ matrix.goos }}" \
"${{ matrix.goarch }}" \
"${{ matrix.goarch_variant }}" \
"${{ matrix.arch }}"
- name: Generate checksums
run: ./scripts/distribute.sh generate-checksums "${{ matrix.arch }}"
- name: Verify build
run: |
./scripts/distribute.sh verify-build "${{ matrix.arch }}"
- name: Upload artifacts
uses: actions/upload-artifact@v4
with:
name: cache-apt-pkgs-${{ matrix.arch }}-${{ steps.version.outputs.commit_sha }}
path: distribute/${{ matrix.arch }}/*
retention-days: 30
create-release:
needs: build-and-release
runs-on: ubuntu-latest
if: success()
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Generate version from commit SHA
id: version
run: |
COMMIT_SHA="${GITHUB_SHA:0:8}"
VERSION="${{ env.VERSION_PREFIX }}-${COMMIT_SHA}"
echo "version=${VERSION}" >> $GITHUB_OUTPUT
echo "commit_sha=${COMMIT_SHA}" >> $GITHUB_OUTPUT
- name: Download all artifacts
uses: actions/download-artifact@v4
with:
path: distribute-artifacts
- name: Reorganize artifacts
run: |
./scripts/distribute.sh reorganize-artifacts
- name: Create or update release
uses: softprops/action-gh-release@v1
with:
tag_name: ${{ steps.version.outputs.version }}
name: "${{ steps.version.outputs.version }}"
generate_release_notes: true
files: |
distribute/x64/*
distribute/arm64/*
distribute/arm/*
distribute/x86/*
draft: false
prerelease: true
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Clean up old pre-releases
run: |
echo "Cleaning up old commit-based releases..."
# Keep last 10 commit-based releases, delete older ones
gh release list --limit 50 --json tagName,isPrerelease | \
jq -r '.[] | select(.isPrerelease == true and (.tagName | startswith("commit-"))) | .tagName' | \
tail -n +11 | \
xargs -I {} gh release delete {} --yes --cleanup-tag || true
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

28
.github/workflows/shellcheck.yml vendored Normal file
View file

@ -0,0 +1,28 @@
name: ShellCheck
on:
pull_request:
types: [opened, synchronize]
push:
branches:
- master
- dev
- staging
permissions:
contents: read
jobs:
shellcheck:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Run ShellCheck
uses: ludeeus/action-shellcheck@master
with:
scandir: '.'
format: gcc
severity: style

3
.gitignore vendored
View file

@ -1,2 +1,3 @@
src/cmd/apt_query/apt_query* src/cmd/apt_query/apt_query*
*.log apt_query*
*.log

31
.shellcheckrc Normal file
View file

@ -0,0 +1,31 @@
# ShellCheck configuration file
# See https://github.com/koalaman/shellcheck/wiki/Directives for more information
# Enable all optional checks
enable=all
# Disable specific warnings that may be acceptable in this project
# SC1090: Can't follow non-constant source. Use a path to a file that can be checked
# (disabled because we use dynamic sourcing with script_dir)
disable=SC1090
# SC1091: Not following: <file> was not specified as input
# (disabled because lib.sh is sourced dynamically)
disable=SC1091
# SC2310: Function invoked in && condition (set -e disabled)
# (acceptable pattern for conditional execution)
disable=SC2310
# SC2311: Bash implicitly disabled set -e in command substitution
# (acceptable pattern - we want to capture output even if function fails)
disable=SC2311
# SC2312: Consider invoking command separately to avoid masking return value
# (many of these are acceptable patterns in command substitutions)
disable=SC2312
# Exclude external files that we don't control
exclude-dir=.git
exclude-dir=testlogs

View file

@ -154,3 +154,84 @@ For more context and information see [issue #57](https://github.com/awalsh128/ca
### Cache Limits ### Cache Limits
A repository can have up to 5GB of caches. Once the 5GB limit is reached, older caches will be evicted based on when the cache was last accessed. Caches that are not accessed within the last week will also be evicted. To get more information on how to access and manage your actions's caches, see [GitHub Actions / Using workflows / Cache dependencies](https://docs.github.com/en/actions/using-workflows/caching-dependencies-to-speed-up-workflows#viewing-cache-entries). A repository can have up to 5GB of caches. Once the 5GB limit is reached, older caches will be evicted based on when the cache was last accessed. Caches that are not accessed within the last week will also be evicted. To get more information on how to access and manage your actions's caches, see [GitHub Actions / Using workflows / Cache dependencies](https://docs.github.com/en/actions/using-workflows/caching-dependencies-to-speed-up-workflows#viewing-cache-entries).
## Development
### Prerequisites
- **Go 1.20+** (for building the `apt_query` binary) - version specified in `go.mod`
- Install from [golang.org](https://golang.org/dl/) or via package manager
- Verify installation: `go version`
- **ShellCheck** (for linting shell scripts) - install via:
- macOS: `brew install shellcheck`
- Linux: `sudo apt-get install shellcheck` or see [shellcheck installation guide](https://github.com/koalaman/shellcheck#installing)
- Windows: Available via [scoop](https://scoop.sh/) or [chocolatey](https://chocolatey.org/)
### Building
The project includes Go binaries (`apt_query-arm64` and `apt_query-x86`) that are used by the shell scripts to query APT package information.
**Build all packages:**
```bash
go build -v ./...
```
**Build for specific architecture:**
```bash
# For ARM64 (Apple Silicon, ARM servers)
GOARCH=arm64 go build -o apt_query-arm64 ./src/cmd/apt_query
# For x86_64 (Intel/AMD)
GOARCH=amd64 go build -o apt_query-x86 ./src/cmd/apt_query
```
**Run tests:**
```bash
go test -v ./...
```
### Linting
This project uses [ShellCheck](https://github.com/koalaman/shellcheck) to ensure shell script quality and catch common errors. The configuration is stored in `.shellcheckrc`.
**Run ShellCheck locally:**
```bash
shellcheck *.sh
```
**IDE Integration:**
Many IDEs and editors can automatically run ShellCheck:
- **VS Code**: Install the [ShellCheck extension](https://marketplace.visualstudio.com/items?itemName=timonwong.shellcheck)
- **Vim/Neovim**: Use [ALE](https://github.com/dense-analysis/ale) or [coc-shellcheck](https://github.com/josa42/coc-shellcheck)
- **IntelliJ/CLion**: ShellCheck is integrated in recent versions
- **Sublime Text**: Install [SublimeLinter-shellcheck](https://github.com/SublimeLinter/SublimeLinter-shellcheck)
**Go Linting:**
This project uses [golangci-lint](https://golangci-lint.run/) for Go code quality checks.
**Run golangci-lint locally:**
```bash
# Install golangci-lint (if not already installed)
# macOS: brew install golangci-lint
# Linux: See https://golangci-lint.run/usage/install/
golangci-lint run
```
**IDE Integration:**
- **VS Code**: Install the [Go extension](https://marketplace.visualstudio.com/items?itemName=golang.go) for syntax highlighting, auto-completion, and built-in linting
- **IntelliJ/GoLand**: Built-in Go support with linting and formatting
- **Vim/Neovim**: Use [vim-go](https://github.com/fatih/vim-go) for Go development
### CI/CD
The GitHub Actions workflows will automatically:
- **Build and test** Go code on pull requests
- **Run ShellCheck** on shell scripts (blocks PRs on failures)
- **Run golangci-lint** on Go code (blocks PRs on failures)
All checks run on pull requests and pushes to `master`, `dev`, and `staging` branches.

View file

@ -81,7 +81,7 @@ runs:
- id: load-cache - id: load-cache
if: ${{ env.CACHE_KEY }} if: ${{ env.CACHE_KEY }}
uses: actions/cache/restore@v5 uses: actions/cache/restore@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
with: with:
path: ~/cache-apt-pkgs path: ~/cache-apt-pkgs
key: cache-apt-pkgs_${{ env.CACHE_KEY }} key: cache-apt-pkgs_${{ env.CACHE_KEY }}
@ -110,14 +110,14 @@ runs:
- id: upload-logs - id: upload-logs
if: ${{ env.CACHE_KEY && inputs.debug == 'true' }} if: ${{ env.CACHE_KEY && inputs.debug == 'true' }}
uses: actions/upload-artifact@v7 uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with: with:
name: cache-apt-pkgs-logs_${{ env.CACHE_KEY }} name: cache-apt-pkgs-logs_${{ env.CACHE_KEY }}
path: ~/cache-apt-pkgs/*.log path: ~/cache-apt-pkgs/*.log
- id: save-cache - id: save-cache
if: ${{ env.CACHE_KEY && ! steps.load-cache.outputs.cache-hit }} if: ${{ env.CACHE_KEY && ! steps.load-cache.outputs.cache-hit }}
uses: actions/cache/save@v5 uses: actions/cache/save@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
with: with:
path: ~/cache-apt-pkgs path: ~/cache-apt-pkgs
key: ${{ steps.load-cache.outputs.cache-primary-key }} key: ${{ steps.load-cache.outputs.cache-primary-key }}

View file

@ -19,7 +19,7 @@ cache_dir="${1}"
add_repository="${3}" add_repository="${3}"
# List of the packages to use. # List of the packages to use.
input_packages="${@:4}" input_packages="${*:4}"
if ! apt-fast --version > /dev/null 2>&1; then if ! apt-fast --version > /dev/null 2>&1; then
log "Installing apt-fast for optimized installs..." log "Installing apt-fast for optimized installs..."
@ -31,7 +31,7 @@ if ! apt-fast --version > /dev/null 2>&1; then
fi fi
# Add custom repositories if specified # Add custom repositories if specified
if [ -n "${add_repository}" ]; then if [[ -n "${add_repository}" ]]; then
log "Adding custom repositories..." log "Adding custom repositories..."
for repository in ${add_repository}; do for repository in ${add_repository}; do
log "- Adding repository: ${repository}" log "- Adding repository: ${repository}"
@ -42,12 +42,7 @@ if [ -n "${add_repository}" ]; then
fi fi
log "Updating APT package list..." log "Updating APT package list..."
if [[ -z "$(find -H /var/lib/apt/lists -maxdepth 0 -mmin -5)" ]]; then update_apt_lists_if_stale
sudo apt-fast update > /dev/null
log "done"
else
log "skipped (fresh within at least 5 minutes)"
fi
log_empty_line log_empty_line
@ -76,7 +71,10 @@ install_log_filepath="${cache_dir}/install.log"
log "Clean installing ${package_count} packages..." log "Clean installing ${package_count} packages..."
# Zero interaction while installing or upgrading the system via apt. # Zero interaction while installing or upgrading the system via apt.
sudo DEBIAN_FRONTEND=noninteractive apt-fast --yes install ${packages} > "${install_log_filepath}" # Note: sudo doesn't affect redirects, but we want the output in the file anyway
# shellcheck disable=SC2024
# We intentionally redirect output here; the redirect happens as the current user which is fine
sudo DEBIAN_FRONTEND=noninteractive apt-fast --yes install "${packages}" > "${install_log_filepath}"
log "done" log "done"
log "Installation log written to ${install_log_filepath}" log "Installation log written to ${install_log_filepath}"
@ -86,7 +84,7 @@ installed_packages=$(get_installed_packages "${install_log_filepath}")
log "Installed package list:" log "Installed package list:"
for installed_package in ${installed_packages}; do for installed_package in ${installed_packages}; do
# Reformat for human friendly reading. # Reformat for human friendly reading.
log "- $(echo ${installed_package} | awk -F\= '{print $1" ("$2")"}')" log "- $(echo "${installed_package}" | awk -F= '{print $1" ("$2")"}')"
done done
log_empty_line log_empty_line
@ -98,20 +96,20 @@ for installed_package in ${installed_packages}; do
# Sanity test in case APT enumerates duplicates. # Sanity test in case APT enumerates duplicates.
if test ! -f "${cache_filepath}"; then if test ! -f "${cache_filepath}"; then
read package_name package_ver < <(get_package_name_ver "${installed_package}") read -r package_name package_ver < <(get_package_name_ver "${installed_package}")
log " * Caching ${package_name} to ${cache_filepath}..." log " * Caching ${package_name} to ${cache_filepath}..."
# Pipe all package files (no folders), including symlinks, their targets, and installation control data to Tar. # Pipe all package files (no folders), including symlinks, their targets, and installation control data to Tar.
tar -cf "${cache_filepath}" -C / --verbatim-files-from --files-from <( tar -cf "${cache_filepath}" -C / --verbatim-files-from --files-from <(
{ dpkg -L "${package_name}" && { dpkg -L "${package_name}" &&
get_install_script_filepath "" "${package_name}" "preinst" && { get_install_script_filepath "/" "${package_name}" "preinst" ;
get_install_script_filepath "" "${package_name}" "postinst" ; } | get_install_script_filepath "/" "${package_name}" "postinst" ; } ; } |
while IFS= read -r f; do while IFS= read -r f; do
if test -f "${f}" -o -L "${f}"; then if test -f "${f}" -o -L "${f}"; then
get_tar_relpath "${f}" get_tar_relpath "${f}"
if [ -L "${f}" ]; then if [[ -L "${f}" ]]; then
target="$(readlink -f "${f}")" target="$(readlink -f "${f}")"
if [ -f "${target}" ]; then if [[ -f "${target}" ]]; then
get_tar_relpath "${target}" get_tar_relpath "${target}"
fi fi
fi fi
@ -125,7 +123,7 @@ for installed_package in ${installed_packages}; do
# Comma delimited name:ver pairs in the all packages manifest. # Comma delimited name:ver pairs in the all packages manifest.
manifest_all="${manifest_all}${package_name}=${package_ver}," manifest_all="${manifest_all}${package_name}=${package_ver},"
done done
log "done (total cache size $(du -h ${cache_dir} | tail -1 | awk '{print $1}'))" log "done (total cache size $(du -h "${cache_dir}" | tail -1 | awk '{print $1}'))"
log_empty_line log_empty_line

81
lib.sh
View file

@ -17,14 +17,16 @@ set +e
# Filepath of the install script, otherwise an empty string. # Filepath of the install script, otherwise an empty string.
############################################################################### ###############################################################################
function execute_install_script { function execute_install_script {
local package_name=$(basename ${2} | awk -F\= '{print $1}') local package_name
local install_script_filepath=$(\ package_name=$(basename "${2}" | awk -F= '{print $1}')
local install_script_filepath
install_script_filepath=$(\
get_install_script_filepath "${1}" "${package_name}" "${3}") get_install_script_filepath "${1}" "${package_name}" "${3}")
if test ! -z "${install_script_filepath}"; then if test ! -z "${install_script_filepath}"; then
log "- Executing ${install_script_filepath}..." log "- Executing ${install_script_filepath}..."
# Don't abort on errors; dpkg-trigger will error normally since it is # Don't abort on errors; dpkg-trigger will error normally since it is
# outside its run environment. # outside its run environment.
sudo sh -x ${install_script_filepath} ${4} || true sudo sh -x "${install_script_filepath}" "${4}" || true
log " done" log " done"
fi fi
} }
@ -40,9 +42,17 @@ function execute_install_script {
############################################################################### ###############################################################################
function get_install_script_filepath { function get_install_script_filepath {
# Filename includes arch (e.g. amd64). # Filename includes arch (e.g. amd64).
local filepath="$(\ local filepath
ls -1 ${1}var/lib/dpkg/info/${2}*.${3} 2> /dev/null \ # Use glob expansion instead of ls|grep for better handling of non-alphanumeric filenames
| grep -E ${2}'(:.*)?.'${3} | head -1 || true)" # Use nullglob to prevent literal match when no files found
shopt -s nullglob
for f in "${1}"var/lib/dpkg/info/"${2}"*."${3}"; do
if [[ -f "${f}" ]] && [[ "${f}" =~ ${2}'(:.*)?.'"${3}" ]]; then
filepath="${f}"
break
fi
done
shopt -u nullglob
test "${filepath}" && echo "${filepath}" test "${filepath}" && echo "${filepath}"
} }
@ -66,7 +76,7 @@ function get_installed_packages {
log_err "Unable to parse package name and version from \"${line}\"" log_err "Unable to parse package name and version from \"${line}\""
exit 2 exit 2
fi fi
done < <(grep "^Unpacking " ${install_log_filepath}) done < <(grep "^Unpacking " "${install_log_filepath}")
if test -n "${dep_packages}"; then if test -n "${dep_packages}"; then
echo "${dep_packages:0:-1}" # Removing trailing space. echo "${dep_packages:0:-1}" # Removing trailing space.
else else
@ -83,13 +93,13 @@ function get_installed_packages {
############################################################################### ###############################################################################
function get_package_name_ver { function get_package_name_ver {
local ORIG_IFS="${IFS}" local ORIG_IFS="${IFS}"
IFS=\= read name ver <<< "${1}" IFS='=' read -r name ver <<< "${1}"
IFS="${ORIG_IFS}" IFS="${ORIG_IFS}"
# If version not found in the fully qualified package value. # If version not found in the fully qualified package value.
if test -z "${ver}"; then if test -z "${ver}"; then
# This is a fallback and should not be used any more as its slow. # This is a fallback and should not be used any more as its slow.
log_err "Unexpected version resolution for package '${name}'" log_err "Unexpected version resolution for package '${name}'"
ver="$(apt-cache show ${name} | grep '^Version:' | awk '{print $2}')" ver="$(apt-cache show "${name}" | grep '^Version:' | awk '{print $2}')"
fi fi
echo "${name}" "${ver}" echo "${name}" "${ver}"
} }
@ -105,16 +115,19 @@ function get_normalized_package_list {
# Remove commas, and block scalar folded backslashes, # Remove commas, and block scalar folded backslashes,
# extraneous spaces at the middle, beginning and end # extraneous spaces at the middle, beginning and end
# then sort. # then sort.
local packages=$(echo "${1}" \ local packages
packages=$(echo "${1}" \
| sed 's/[,\]/ /g; s/\s\+/ /g; s/^\s\+//g; s/\s\+$//g' \ | sed 's/[,\]/ /g; s/\s\+/ /g; s/^\s\+//g; s/\s\+$//g' \
| sort -t' ') | sort -t' ')
local script_dir="$(dirname -- "$(realpath -- "${0}")")" local script_dir
script_dir="$(dirname -- "$(realpath -- "${0}")")"
local architecture=$(dpkg --print-architecture) local architecture
if [ "${architecture}" == "arm64" ]; then architecture=$(dpkg --print-architecture)
${script_dir}/apt_query-arm64 normalized-list ${packages} if [[ "${architecture}" == "arm64" ]]; then
"${script_dir}"/apt_query-arm64 normalized-list "${packages}"
else else
${script_dir}/apt_query-x86 normalized-list ${packages} "${script_dir}"/apt_query-x86 normalized-list "${packages}"
fi fi
} }
@ -127,14 +140,42 @@ function get_normalized_package_list {
# The relative filepath to archive. # The relative filepath to archive.
############################################################################### ###############################################################################
function get_tar_relpath { function get_tar_relpath {
local filepath=${1} local filepath="${1}"
if test ${filepath:0:1} = "/"; then if test "${filepath:0:1}" = "/"; then
echo "${filepath:1}" echo "${filepath:1}"
else else
echo "${filepath}" echo "${filepath}"
fi fi
} }
###############################################################################
# Updates APT package lists if they are stale (modified more than 5 minutes ago).
# This ensures compatibility with environments like nektos/act where package lists
# may be empty or stale. Only runs when ACT environment variable is set (nektos/act)
# to avoid slowing down normal GitHub Actions runs.
# Arguments:
# None
# Returns:
# None
###############################################################################
function update_apt_lists_if_stale {
# Only check for stale package lists when running in nektos/act
# GitHub Actions runners have fresh package lists, so skip this overhead
if [ "${ACT}" = "true" ]; then
if [[ -z "$(find -H /var/lib/apt/lists -maxdepth 0 -mmin -5 2>/dev/null)" ]]; then
log "APT package lists are stale, updating..."
if command -v apt-fast > /dev/null 2>&1; then
sudo apt-fast update > /dev/null 2>&1 || sudo apt update > /dev/null 2>&1 || true
else
sudo apt update > /dev/null 2>&1 || true
fi
log "APT package lists updated"
else
log "APT package lists are fresh (within 5 minutes), skipping update"
fi
fi
}
function log { echo "${@}"; } function log { echo "${@}"; }
function log_err { >&2 echo "${@}"; } function log_err { >&2 echo "${@}"; }
@ -153,7 +194,7 @@ function validate_bool {
if test "${1}" != "true" -a "${1}" != "false"; then if test "${1}" != "true" -a "${1}" != "false"; then
log "aborted" log "aborted"
log "${2} value '${1}' must be either true or false (case sensitive)." log "${2} value '${1}' must be either true or false (case sensitive)."
exit ${3} exit "${3}"
fi fi
} }
@ -167,12 +208,12 @@ function validate_bool {
# Log lines from write. # Log lines from write.
############################################################################### ###############################################################################
function write_manifest { function write_manifest {
if [ ${#2} -eq 0 ]; then if [[ ${#2} -eq 0 ]]; then
log "Skipped ${1} manifest write. No packages to install." log "Skipped ${1} manifest write. No packages to install."
else else
log "Writing ${1} packages manifest to ${3}..." log "Writing ${1} packages manifest to ${3}..."
# 0:-1 to remove trailing comma, delimit by newline and sort. # 0:-1 to remove trailing comma, delimit by newline and sort.
echo "${2:0:-1}" | tr ',' '\n' | sort > ${3} echo "${2:0:-1}" | tr ',' '\n' | sort > "${3}"
log "done" log "done"
fi fi
} }

View file

@ -29,12 +29,12 @@ test "${debug}" = "true" && set -x
add_repository="${6}" add_repository="${6}"
# List of the packages to use. # List of the packages to use.
packages="${@:7}" packages="${*:7}"
if test "${cache_hit}" = "true"; then if test "${cache_hit}" = "true"; then
${script_dir}/restore_pkgs.sh "${cache_dir}" "${cache_restore_root}" "${execute_install_scripts}" "${debug}" "${script_dir}"/restore_pkgs.sh "${cache_dir}" "${cache_restore_root}" "${execute_install_scripts}" "${debug}"
else else
${script_dir}/install_and_cache_pkgs.sh "${cache_dir}" "${debug}" "${add_repository}" ${packages} "${script_dir}"/install_and_cache_pkgs.sh "${cache_dir}" "${debug}" "${add_repository}" "${packages}"
fi fi
log_empty_line log_empty_line

View file

@ -10,7 +10,7 @@ source "${script_dir}/lib.sh"
# Setup first before other operations. # Setup first before other operations.
debug="${4}" debug="${4}"
validate_bool "${debug}" debug 1 validate_bool "${debug}" debug 1
test ${debug} == "true" && set -x test "${debug}" == "true" && set -x
# Directory that holds the cached packages. # Directory that holds the cached packages.
cache_dir="${1}" cache_dir="${1}"
@ -28,15 +28,20 @@ debug="${4}"
add_repository="${5}" add_repository="${5}"
# List of the packages to use. # List of the packages to use.
input_packages="${@:6}" # Use * instead of @ to concatenate array elements into a single string
input_packages="${*:6}"
# Trim commas, excess spaces, and sort. # Trim commas, excess spaces, and sort.
log "Normalizing package list..." log "Normalizing package list..."
# Ensure APT package lists are updated if stale (for nektos/act compatibility)
update_apt_lists_if_stale
packages="$(get_normalized_package_list "${input_packages}")" packages="$(get_normalized_package_list "${input_packages}")"
log "done" log "done"
# Create cache directory so artifacts can be saved. # Create cache directory so artifacts can be saved.
mkdir -p ${cache_dir} mkdir -p "${cache_dir}"
log "Validating action arguments (version='${version}', packages='${packages}')..."; log "Validating action arguments (version='${version}', packages='${packages}')...";
if grep -q " " <<< "${version}"; then if grep -q " " <<< "${version}"; then
@ -47,7 +52,9 @@ fi
# Is length of string zero? # Is length of string zero?
if test -z "${packages}"; then if test -z "${packages}"; then
case "$EMPTY_PACKAGES_BEHAVIOR" in # shellcheck disable=SC2154
# EMPTY_PACKAGES_BEHAVIOR is an environment variable passed from GitHub Actions
case "${EMPTY_PACKAGES_BEHAVIOR}" in
ignore) ignore)
exit 0 exit 0
;; ;;
@ -66,7 +73,7 @@ fi
validate_bool "${execute_install_scripts}" execute_install_scripts 4 validate_bool "${execute_install_scripts}" execute_install_scripts 4
# Basic validation for repository parameter # Basic validation for repository parameter
if [ -n "${add_repository}" ]; then if [[ -n "${add_repository}" ]]; then
log "Validating repository parameter..." log "Validating repository parameter..."
for repository in ${add_repository}; do for repository in ${add_repository}; do
# Check if repository format looks valid (basic check) # Check if repository format looks valid (basic check)
@ -100,13 +107,13 @@ log "- CPU architecture is '${cpu_arch}'."
value="${packages} @ ${version} ${force_update_inc}" value="${packages} @ ${version} ${force_update_inc}"
# Include repositories in cache key to ensure different repos get different caches # Include repositories in cache key to ensure different repos get different caches
if [ -n "${add_repository}" ]; then if [[ -n "${add_repository}" ]]; then
value="${value} ${add_repository}" value="${value} ${add_repository}"
log "- Repositories '${add_repository}' added to value." log "- Repositories '${add_repository}' added to value."
fi fi
# Don't invalidate existing caches for the standard Ubuntu runners # Don't invalidate existing caches for the standard Ubuntu runners
if [ "${cpu_arch}" != "x86_64" ]; then if [[ "${cpu_arch}" != "x86_64" ]]; then
value="${value} ${cpu_arch}" value="${value} ${cpu_arch}"
log "- Architecture '${cpu_arch}' added to value." log "- Architecture '${cpu_arch}' added to value."
fi fi
@ -119,5 +126,5 @@ log "- Value hashed as '${key}'."
log "done" log "done"
key_filepath="${cache_dir}/cache_key.md5" key_filepath="${cache_dir}/cache_key.md5"
echo ${key} > ${key_filepath} echo "${key}" > "${key_filepath}"
log "Hash value written to ${key_filepath}" log "Hash value written to ${key_filepath}"

View file

@ -6,7 +6,7 @@ set -e
# Debug mode for diagnosing issues. # Debug mode for diagnosing issues.
# Setup first before other operations. # Setup first before other operations.
debug="${4}" debug="${4}"
test ${debug} == "true" && set -x test "${debug}" == "true" && set -x
# Include library. # Include library.
script_dir="$(dirname -- "$(realpath -- "${0}")")" script_dir="$(dirname -- "$(realpath -- "${0}")")"
@ -18,30 +18,33 @@ cache_dir="${1}"
# Root directory to untar the cached packages to. # Root directory to untar the cached packages to.
# Typically filesystem root '/' but can be changed for testing. # Typically filesystem root '/' but can be changed for testing.
cache_restore_root="${2}" cache_restore_root="${2}"
test -d ${cache_restore_root} || mkdir ${cache_restore_root} test -d "${cache_restore_root}" || mkdir "${cache_restore_root}"
# Cache and execute post install scripts on restore. # Cache and execute post install scripts on restore.
execute_install_scripts="${3}" execute_install_scripts="${3}"
cache_filepaths="$(ls -1 "${cache_dir}" | sort)" # Use find instead of ls to better handle non-alphanumeric filenames
log "Found $(echo ${cache_filepaths} | wc -w) files in the cache." cache_filepaths="$(find "${cache_dir}" -mindepth 1 -maxdepth 1 -type f -o -type d | sort)"
file_count=$(echo "${cache_filepaths}" | wc -w)
log "Found ${file_count} files in the cache."
for cache_filepath in ${cache_filepaths}; do for cache_filepath in ${cache_filepaths}; do
log "- "$(basename ${cache_filepath})"" log "- $(basename "${cache_filepath}")"
done done
log_empty_line log_empty_line
log "Reading from main requested packages manifest..." log "Reading from main requested packages manifest..."
for logline in $(cat "${cache_dir}/manifest_main.log" | tr ',' '\n' ); do while IFS= read -r logline; do
log "- $(echo "${logline}" | tr ':' ' ')" log "- $(echo "${logline}" | tr ':' ' ')"
done done < <(tr ',' '\n' < "${cache_dir}/manifest_main.log")
log "done" log "done"
log_empty_line log_empty_line
# Only search for archived results. Manifest and cache key also live here. # Only search for archived results. Manifest and cache key also live here.
cached_filepaths=$(ls -1 "${cache_dir}"/*.tar 2>/dev/null | sort) # Use find instead of ls to better handle non-alphanumeric filenames
cached_filecount=$(echo ${cached_filepaths} | wc -w) cached_filepaths=$(find "${cache_dir}" -maxdepth 1 -name "*.tar" -type f 2>/dev/null | sort)
cached_filecount=$(echo "${cached_filepaths}" | wc -w)
log "Restoring ${cached_filecount} packages from cache..." log "Restoring ${cached_filecount} packages from cache..."
for cached_filepath in ${cached_filepaths}; do for cached_filepath in ${cached_filepaths}; do
@ -51,7 +54,7 @@ for cached_filepath in ${cached_filepaths}; do
log " done" log " done"
# Execute install scripts if available. # Execute install scripts if available.
if test ${execute_install_scripts} == "true"; then if test "${execute_install_scripts}" == "true"; then
# May have to add more handling for extracting pre-install script before extracting all files. # May have to add more handling for extracting pre-install script before extracting all files.
# Keeping it simple for now. # Keeping it simple for now.
execute_install_script "${cache_restore_root}" "${cached_filepath}" preinst install execute_install_script "${cache_restore_root}" "${cached_filepath}" preinst install

View file

@ -10,10 +10,10 @@
EXECUTION-OBJ-END EXECUTION-OBJ-END
2025/03/15 22:29:14 EXECUTION-OBJ-START 2025/03/15 22:29:14 EXECUTION-OBJ-START
{ {
"Cmd": "bash -c apt-cache showpkg libvips | grep -A 1 \"Reverse Provides\" | tail -1", "Cmd": "apt-cache showpkg libvips",
"Stdout": "libvips42 8.9.1-2 (= )\n", "Stdout": "Package: libvips\nReverse Provides:\nlibvips42 8.9.1-2 (= )\n",
"Stderr": "", "Stderr": "",
"CombinedOut": "libvips42 8.9.1-2 (= )\n", "CombinedOut": "Package: libvips\nReverse Provides:\nlibvips42 8.9.1-2 (= )\n",
"ExitCode": 0 "ExitCode": 0
} }
EXECUTION-OBJ-END EXECUTION-OBJ-END

View file

@ -33,20 +33,36 @@ func isErrLine(line string) bool {
// Resolves virtual packages names to their concrete one. // Resolves virtual packages names to their concrete one.
func getNonVirtualPackage(executor exec.Executor, name string) (pkg *AptPackage, err error) { func getNonVirtualPackage(executor exec.Executor, name string) (pkg *AptPackage, err error) {
execution := executor.Exec("bash", "-c", fmt.Sprintf("apt-cache showpkg %s | grep -A 1 \"Reverse Provides\" | tail -1", name)) execution := executor.Exec("apt-cache", "showpkg", name)
err = execution.Error() err = execution.Error()
if err != nil { if err != nil {
logging.Fatal(err) logging.Fatal(err)
return pkg, err return pkg, err
} }
if isErrLine(execution.CombinedOut) {
return pkg, execution.Error() inReverseProvides := false
for _, line := range strings.Split(execution.CombinedOut, "\n") {
trimmed := strings.TrimSpace(line)
if trimmed == "" {
continue
}
if trimmed == "Reverse Provides:" {
inReverseProvides = true
continue
}
if !inReverseProvides || strings.HasPrefix(trimmed, "W: ") || isErrLine(trimmed) {
continue
}
if strings.HasSuffix(trimmed, ":") {
break
}
splitLine := GetSplitLine(trimmed, " ", 3)
if len(splitLine.Words) < 2 {
continue
}
return &AptPackage{Name: splitLine.Words[0], Version: splitLine.Words[1]}, nil
} }
splitLine := GetSplitLine(execution.CombinedOut, " ", 3) return pkg, fmt.Errorf("unable to parse reverse provides package name and version from apt-cache showpkg output below:\n%s", execution.CombinedOut)
if len(splitLine.Words) < 2 {
return pkg, fmt.Errorf("unable to parse space delimited line's package name and version from apt-cache showpkg output below:\n%s", execution.CombinedOut)
}
return &AptPackage{Name: splitLine.Words[0], Version: splitLine.Words[1]}, nil
} }
func getPackage(executor exec.Executor, paragraph string) (pkg *AptPackage, err error) { func getPackage(executor exec.Executor, paragraph string) (pkg *AptPackage, err error) {

View file

@ -0,0 +1,53 @@
package common
import (
"fmt"
"strings"
"testing"
execpkg "awalsh128.com/cache-apt-pkgs-action/src/internal/exec"
)
type mockExecutor struct {
executions map[string]*execpkg.Execution
}
func (s mockExecutor) Exec(name string, arg ...string) *execpkg.Execution {
cmd := name + " " + strings.Join(arg, " ")
execution, ok := s.executions[cmd]
if !ok {
panic(fmt.Sprintf("unexpected command: %s", cmd))
}
return execution
}
func TestGetNonVirtualPackage_WithWarningsInReverseProvides(t *testing.T) {
executor := mockExecutor{
executions: map[string]*execpkg.Execution{
"apt-cache showpkg libopenblas0-openmp": {
Cmd: "apt-cache showpkg libopenblas0-openmp",
CombinedOut: strings.Join([]string{
"Package: libopenblas0-openmp",
"Reverse Provides:",
"libopenblas0-openmp 0.3.26+ds-1ubuntu0.1 (= )",
"W: Unable to read /etc/apt/apt.conf.d/99github-actions - open (13: Permission denied)",
"",
}, "\n"),
ExitCode: 0,
},
},
}
pkg, err := getNonVirtualPackage(executor, "libopenblas0-openmp")
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if pkg == nil {
t.Fatal("expected package but got nil")
}
expected := AptPackage{Name: "libopenblas0-openmp", Version: "0.3.26+ds-1ubuntu0.1"}
if *pkg != expected {
t.Fatalf("unexpected package.\nexpected: %+v\nactual: %+v", expected, *pkg)
}
}