Skip to content

Commit 8e7def4

Browse files
committed
feat: shared action use nix derivation to check changes and run test if change in inputs
1 parent 7fae2ab commit 8e7def4

5 files changed

Lines changed: 225 additions & 79 deletions

File tree

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
name: Check Docker Image Changes
2+
description: Determines if Docker image inputs have changed between current and base branch
3+
4+
inputs:
5+
base_ref:
6+
description: 'Base branch ref for comparison (typically github.base_ref)'
7+
required: false
8+
default: ''
9+
event_name:
10+
description: 'GitHub event name (typically github.event_name)'
11+
required: true
12+
13+
outputs:
14+
should_run:
15+
description: 'Whether tests should run based on input changes'
16+
value: ${{ steps.check.outputs.should_run }}
17+
input_hash:
18+
description: 'Current Docker image inputs hash'
19+
value: ${{ steps.check.outputs.input_hash }}
20+
base_hash:
21+
description: 'Base branch Docker image inputs hash (empty if not a PR)'
22+
value: ${{ steps.check.outputs.base_hash }}
23+
24+
runs:
25+
using: composite
26+
steps:
27+
- name: Get current Docker image inputs hash
28+
id: current
29+
shell: bash
30+
run: |
31+
HASH=$(nix run --accept-flake-config .#docker-image-inputs -- hash)
32+
echo "hash=$HASH" >> "$GITHUB_OUTPUT"
33+
echo "Current Docker image inputs hash: $HASH"
34+
35+
- name: Get base branch Docker image inputs hash
36+
id: base
37+
if: inputs.event_name == 'pull_request'
38+
shell: bash
39+
run: |
40+
# Fetch base branch
41+
git fetch origin ${{ inputs.base_ref }} --depth=1
42+
43+
# Checkout base branch files temporarily
44+
git checkout FETCH_HEAD -- . 2>/dev/null || true
45+
46+
# Get hash from base branch
47+
BASE_HASH=$(nix run --accept-flake-config .#docker-image-inputs -- hash 2>/dev/null || echo "")
48+
49+
# Restore current branch
50+
git checkout HEAD -- .
51+
52+
echo "hash=$BASE_HASH" >> "$GITHUB_OUTPUT"
53+
echo "Base branch Docker image inputs hash: $BASE_HASH"
54+
55+
- name: Determine if tests should run
56+
id: check
57+
shell: bash
58+
run: |
59+
CURRENT_HASH="${{ steps.current.outputs.hash }}"
60+
BASE_HASH="${{ steps.base.outputs.hash }}"
61+
62+
echo "input_hash=$CURRENT_HASH" >> "$GITHUB_OUTPUT"
63+
echo "base_hash=$BASE_HASH" >> "$GITHUB_OUTPUT"
64+
65+
if [[ "${{ inputs.event_name }}" == "workflow_dispatch" ]]; then
66+
echo "Workflow dispatch - running tests"
67+
echo "should_run=true" >> "$GITHUB_OUTPUT"
68+
elif [[ "${{ inputs.event_name }}" == "push" ]]; then
69+
echo "Push to protected branch - running tests"
70+
echo "should_run=true" >> "$GITHUB_OUTPUT"
71+
elif [[ -z "$BASE_HASH" ]]; then
72+
echo "Could not get base hash - running tests to be safe"
73+
echo "should_run=true" >> "$GITHUB_OUTPUT"
74+
elif [[ "$CURRENT_HASH" != "$BASE_HASH" ]]; then
75+
echo "Docker image inputs changed ($BASE_HASH -> $CURRENT_HASH) - running tests"
76+
echo "should_run=true" >> "$GITHUB_OUTPUT"
77+
else
78+
echo "Docker image inputs unchanged - skipping tests"
79+
echo "should_run=false" >> "$GITHUB_OUTPUT"
80+
fi

.github/workflows/cli-smoke-test.yml

Lines changed: 5 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -33,57 +33,12 @@ jobs:
3333
DEV_AWS_ROLE: ${{ secrets.DEV_AWS_ROLE }}
3434
NIX_SIGN_SECRET_KEY: ${{ secrets.NIX_SIGN_SECRET_KEY }}
3535

36-
- name: Get current Docker image inputs hash
37-
id: current
38-
run: |
39-
HASH=$(nix run --accept-flake-config .#docker-image-inputs -- hash)
40-
echo "hash=$HASH" >> "$GITHUB_OUTPUT"
41-
echo "Current Docker image inputs hash: $HASH"
42-
43-
- name: Get base branch Docker image inputs hash
44-
id: base
45-
if: github.event_name == 'pull_request'
46-
run: |
47-
# Fetch base branch
48-
git fetch origin ${{ github.base_ref }} --depth=1
49-
50-
# Checkout base branch files temporarily
51-
git checkout FETCH_HEAD -- . 2>/dev/null || true
52-
53-
# Get hash from base branch
54-
BASE_HASH=$(nix run --accept-flake-config .#docker-image-inputs -- hash 2>/dev/null || echo "")
55-
56-
# Restore current branch
57-
git checkout HEAD -- .
58-
59-
echo "hash=$BASE_HASH" >> "$GITHUB_OUTPUT"
60-
echo "Base branch Docker image inputs hash: $BASE_HASH"
61-
62-
- name: Determine if tests should run
36+
- name: Check Docker image changes
6337
id: check
64-
run: |
65-
CURRENT_HASH="${{ steps.current.outputs.hash }}"
66-
BASE_HASH="${{ steps.base.outputs.hash }}"
67-
68-
echo "input_hash=$CURRENT_HASH" >> "$GITHUB_OUTPUT"
69-
echo "base_hash=$BASE_HASH" >> "$GITHUB_OUTPUT"
70-
71-
if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then
72-
echo "Workflow dispatch - running tests"
73-
echo "should_run=true" >> "$GITHUB_OUTPUT"
74-
elif [[ "${{ github.event_name }}" == "push" ]]; then
75-
echo "Push to protected branch - running tests"
76-
echo "should_run=true" >> "$GITHUB_OUTPUT"
77-
elif [[ -z "$BASE_HASH" ]]; then
78-
echo "Could not get base hash - running tests to be safe"
79-
echo "should_run=true" >> "$GITHUB_OUTPUT"
80-
elif [[ "$CURRENT_HASH" != "$BASE_HASH" ]]; then
81-
echo "Docker image inputs changed ($BASE_HASH -> $CURRENT_HASH) - running tests"
82-
echo "should_run=true" >> "$GITHUB_OUTPUT"
83-
else
84-
echo "Docker image inputs unchanged - skipping tests"
85-
echo "should_run=false" >> "$GITHUB_OUTPUT"
86-
fi
38+
uses: ./.github/actions/check-docker-image-changes
39+
with:
40+
event_name: ${{ github.event_name }}
41+
base_ref: ${{ github.base_ref }}
8742

8843
cli-smoke-test:
8944
name: CLI Smoke Test (PG ${{ matrix.pg_version }})

.github/workflows/docker-image-test.yml

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
name: Docker Image Test
22

33
on:
4+
pull_request:
5+
types: [opened, reopened, synchronize]
6+
push:
7+
branches:
8+
- develop
9+
- release/*
410
workflow_call:
511
secrets:
612
DEV_AWS_ROLE:
@@ -20,8 +26,35 @@ permissions:
2026
contents: read
2127

2228
jobs:
29+
check-changes:
30+
name: Check Docker Image Changes
31+
runs-on: blacksmith-2vcpu-ubuntu-2404
32+
outputs:
33+
should_run: ${{ steps.check.outputs.should_run }}
34+
input_hash: ${{ steps.check.outputs.input_hash }}
35+
steps:
36+
- name: Checkout Repo
37+
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
38+
39+
- name: Install nix
40+
uses: ./.github/actions/nix-install-ephemeral
41+
with:
42+
push-to-cache: 'false'
43+
env:
44+
DEV_AWS_ROLE: ${{ secrets.DEV_AWS_ROLE }}
45+
NIX_SIGN_SECRET_KEY: ${{ secrets.NIX_SIGN_SECRET_KEY }}
46+
47+
- name: Check Docker image changes
48+
id: check
49+
uses: ./.github/actions/check-docker-image-changes
50+
with:
51+
event_name: ${{ github.event_name }}
52+
base_ref: ${{ github.base_ref }}
53+
2354
docker-image-test:
2455
name: Test ${{ matrix.dockerfile }}
56+
needs: check-changes
57+
if: needs.check-changes.outputs.should_run == 'true'
2558
runs-on: large-linux-arm
2659
timeout-minutes: 120
2760
strategy:
@@ -98,3 +131,14 @@ jobs:
98131
docker rmi "supabase-postgres:${{ matrix.dockerfile }}-test" || true
99132
docker rmi "supabase-postgres:${VERSION}-analyze" || true
100133
docker rmi "pg-docker-test:${VERSION}" || true
134+
135+
skip-notification:
136+
name: Docker Image Test (Skipped)
137+
needs: check-changes
138+
if: needs.check-changes.outputs.should_run == 'false'
139+
runs-on: ubuntu-latest
140+
steps:
141+
- name: Report skipped
142+
run: |
143+
echo "Docker image tests skipped - inputs unchanged"
144+
echo "Input hash: ${{ needs.check-changes.outputs.input_hash }}"

nix/packages/default.nix

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,12 @@
4040
build-test-ami = pkgs.callPackage ./build-test-ami.nix { packer = self'.packages.packer; };
4141
cleanup-ami = pkgs.callPackage ./cleanup-ami.nix { };
4242
dbmate-tool = pkgs.callPackage ./dbmate-tool.nix { inherit (self.supabase) defaults; };
43-
docker-image-inputs = pkgs.callPackage ./docker-image-inputs.nix { };
43+
docker-image-inputs = pkgs.callPackage ./docker-image-inputs.nix {
44+
psql_15_slim = self'.packages."psql_15_slim/bin";
45+
psql_17_slim = self'.packages."psql_17_slim/bin";
46+
psql_orioledb-17_slim = self'.packages."psql_orioledb-17_slim/bin";
47+
supabase-groonga = self'.packages.supabase-groonga;
48+
};
4449
docs = pkgs.callPackage ./docs.nix { };
4550
pgbouncer = pkgs.callPackage ../pgbouncer.nix { };
4651
github-matrix = pkgs.callPackage ./github-matrix {

nix/packages/docker-image-inputs.nix

Lines changed: 90 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,20 @@
22
lib,
33
stdenv,
44
writeShellApplication,
5+
writeText,
56
jq,
7+
# Slim packages used in Docker images
8+
psql_15_slim,
9+
psql_17_slim,
10+
psql_orioledb-17_slim,
11+
# Groonga is also installed in images
12+
supabase-groonga,
613
}:
714

815
let
916
root = ../..;
1017

11-
# Bundle all files that affect Docker image builds
12-
# When any of these change, the derivation hash changes
18+
# Bundle all source files that are copied into Docker images
1319
dockerSources = stdenv.mkDerivation {
1420
name = "docker-image-sources";
1521
src = lib.fileset.toSource {
@@ -30,19 +36,6 @@ let
3036

3137
# Database migrations (copied into images)
3238
(root + "/migrations/db")
33-
34-
# Nix flake (defines the psql packages used in images)
35-
(root + "/flake.nix")
36-
(root + "/flake.lock")
37-
38-
# Nix package definitions that affect slim images
39-
(root + "/nix/packages")
40-
(root + "/nix/config.nix")
41-
(root + "/nix/overlays")
42-
43-
# PostgreSQL and extension definitions
44-
(root + "/nix/ext")
45-
(root + "/nix/postgresql")
4639
];
4740
};
4841

@@ -55,6 +48,54 @@ let
5548
cp -r . $out/
5649
'';
5750
};
51+
52+
# Create a manifest of all package store paths
53+
# This ensures the hash changes when any package changes
54+
packageManifest = writeText "docker-image-packages-manifest" ''
55+
# Slim PostgreSQL packages installed in Docker images
56+
psql_15_slim=${psql_15_slim}
57+
psql_17_slim=${psql_17_slim}
58+
psql_orioledb-17_slim=${psql_orioledb-17_slim}
59+
60+
# Groonga (installed in all images)
61+
supabase-groonga=${supabase-groonga}
62+
'';
63+
64+
# Combined derivation that depends on both sources and packages
65+
dockerImageInputs = stdenv.mkDerivation {
66+
name = "docker-image-inputs";
67+
68+
# No source needed - we just create a manifest
69+
dontUnpack = true;
70+
71+
# These are the actual dependencies that affect the hash
72+
buildInputs = [
73+
dockerSources
74+
psql_15_slim
75+
psql_17_slim
76+
psql_orioledb-17_slim
77+
supabase-groonga
78+
];
79+
80+
installPhase = ''
81+
mkdir -p $out
82+
83+
# Include source files reference
84+
echo "sources=${dockerSources}" > $out/manifest
85+
86+
# Include package manifest
87+
cat ${packageManifest} >> $out/manifest
88+
89+
# Create a combined hash from all inputs
90+
echo "" >> $out/manifest
91+
echo "# Combined input paths:" >> $out/manifest
92+
echo "${dockerSources}" >> $out/manifest
93+
echo "${psql_15_slim}" >> $out/manifest
94+
echo "${psql_17_slim}" >> $out/manifest
95+
echo "${psql_orioledb-17_slim}" >> $out/manifest
96+
echo "${supabase-groonga}" >> $out/manifest
97+
'';
98+
};
5899
in
59100
writeShellApplication {
60101
name = "docker-image-inputs-hash";
@@ -64,24 +105,42 @@ writeShellApplication {
64105
text = ''
65106
set -euo pipefail
66107
67-
DOCKER_SOURCES="${dockerSources}"
68-
INPUT_HASH=$(basename "$DOCKER_SOURCES" | cut -d- -f1)
108+
DOCKER_INPUTS="${dockerImageInputs}"
109+
INPUT_HASH=$(basename "$DOCKER_INPUTS" | cut -d- -f1)
69110
70111
case "''${1:-hash}" in
71112
hash)
72113
echo "$INPUT_HASH"
73114
;;
74115
path)
75-
echo "$DOCKER_SOURCES"
116+
echo "$DOCKER_INPUTS"
117+
;;
118+
manifest)
119+
cat "$DOCKER_INPUTS/manifest"
76120
;;
77121
json)
78122
jq -n \
79123
--arg hash "$INPUT_HASH" \
80-
--arg path "$DOCKER_SOURCES" \
81-
'{hash: $hash, path: $path}'
124+
--arg path "$DOCKER_INPUTS" \
125+
--arg sources "${dockerSources}" \
126+
--arg psql_15_slim "${psql_15_slim}" \
127+
--arg psql_17_slim "${psql_17_slim}" \
128+
--arg psql_orioledb_17_slim "${psql_orioledb-17_slim}" \
129+
--arg supabase_groonga "${supabase-groonga}" \
130+
'{
131+
hash: $hash,
132+
path: $path,
133+
sources: $sources,
134+
packages: {
135+
psql_15_slim: $psql_15_slim,
136+
psql_17_slim: $psql_17_slim,
137+
"psql_orioledb-17_slim": $psql_orioledb_17_slim,
138+
"supabase-groonga": $supabase_groonga
139+
}
140+
}'
82141
;;
83142
*)
84-
echo "Usage: docker-image-inputs-hash [hash|path|json]" >&2
143+
echo "Usage: docker-image-inputs-hash [hash|path|manifest|json]" >&2
85144
exit 1
86145
;;
87146
esac
@@ -90,15 +149,18 @@ writeShellApplication {
90149
meta = {
91150
description = "Get the content hash of all Docker image inputs";
92151
longDescription = ''
93-
This package bundles all source files that affect Docker image builds.
94-
The hash is computed from the Nix store path, which changes when any
95-
input file changes. Use this to detect when Docker images need to be
96-
rebuilt and tested.
152+
This package tracks all inputs that affect Docker image builds:
153+
- Source files: Dockerfiles, configs, migrations
154+
- Nix packages: psql_*_slim, supabase-groonga
155+
156+
The hash changes when ANY of these change, including transitive
157+
dependencies of the Nix packages.
97158
98159
Usage:
99-
docker-image-inputs-hash hash # Get just the hash
100-
docker-image-inputs-hash path # Get the Nix store path
101-
docker-image-inputs-hash json # Get both as JSON
160+
docker-image-inputs-hash hash # Get just the hash
161+
docker-image-inputs-hash path # Get the Nix store path
162+
docker-image-inputs-hash manifest # Show all tracked inputs
163+
docker-image-inputs-hash json # Get detailed JSON output
102164
'';
103165
};
104166
}

0 commit comments

Comments
 (0)