@@ -4,14 +4,32 @@ import { describe, expect, test } from "bun:test";
44
55const repoRoot = join ( import . meta. dir , ".." ) ;
66
7+ const SOURCE_REPO_URL = "https://github.com/terion-name/terrarium" ;
78const OAUTH2_PROXY_DIGEST = "sha256:8f4e89762735e7ec7c3f1bbdd5da4dcd55358db8c3278bfbc2e46a7f86ab7d9e" ;
8- const OAUTH2_PROXY_MIRROR_DIGEST = "sha256:c5ec2ff7b486e72e7e6868efdc4c058f6280dba2ea472751c639d7b0e2bd43de" ;
9- const POSTGRES_DIGEST = "sha256:53e316c761bfcaae02cdc6015c3a11a747fe9d0cde9cd0c3d4c871326862e7ed" ;
9+ const POSTGRES_DIGEST = "sha256:a8da88e1ff62d2764fc63b0f1b0f912ff06fc629d964a260d876be615bd0857b" ;
10+ const OAUTH2_PROXY_DESCRIPTION =
11+ "Mirror of Docker Hardened Image dhi.io/oauth2-proxy:7.15.2-debian13 for pulling without Docker authentication. Original: https://hub.docker.com/hardened-images/catalog/dhi/oauth2-proxy" ;
12+ const POSTGRES_DESCRIPTION =
13+ "Mirror of Docker Hardened Image dhi.io/postgres:17.10-alpine3.22 for pulling without Docker authentication. Original: https://hub.docker.com/hardened-images/catalog/dhi/postgres" ;
14+
15+ const workflowPath = join ( repoRoot , ".github/workflows/mirror-hardened-images.yml" ) ;
16+ const scriptPath = join ( repoRoot , ".github/scripts/mirror-dhi-image.sh" ) ;
17+
18+ const readWorkflow = ( ) => readFileSync ( workflowPath , "utf8" ) ;
19+ const readScript = ( ) => readFileSync ( scriptPath , "utf8" ) ;
20+
21+ const requiredAnnotationKeys = [
22+ "org.opencontainers.image.description" ,
23+ "org.opencontainers.image.source" ,
24+ "org.opencontainers.image.url" ,
25+ "io.terrarium.dhi.original-ref" ,
26+ "io.terrarium.dhi.original-url" ,
27+ ] as const ;
1028
1129describe ( "Docker Hardened Image mirror workflow" , ( ) => {
1230 test ( "mirrors pinned multi-arch DHI indexes to GHCR with digest checks" , ( ) => {
13- const workflow = readFileSync ( join ( repoRoot , ".github/workflows/mirror-hardened-images.yml" ) , "utf8" ) ;
14- const script = readFileSync ( join ( repoRoot , ".github/scripts/mirror-dhi-image.sh" ) , "utf8" ) ;
31+ const workflow = readWorkflow ( ) ;
32+ const script = readScript ( ) ;
1533
1634 expect ( workflow ) . toContain ( "packages: write" ) ;
1735 expect ( workflow ) . toContain ( "DOCKERHUB_USERNAME" ) ;
@@ -20,18 +38,19 @@ describe("Docker Hardened Image mirror workflow", () => {
2038 expect ( workflow ) . toContain ( "workflow_dispatch" ) ;
2139 expect ( workflow ) . toContain ( "skopeo login dhi.io" ) ;
2240 expect ( workflow ) . toContain ( "skopeo login ghcr.io" ) ;
41+ expect ( workflow ) . toContain ( "docker login ghcr.io" ) ;
2342 expect ( workflow ) . toContain ( "/user/packages/container/${PACKAGE_NAME}/visibility" ) ;
2443 expect ( workflow ) . toContain ( "/orgs/${GITHUB_REPOSITORY_OWNER}/packages/container/${PACKAGE_NAME}/visibility" ) ;
25- expect ( workflow ) . toContain ( `dhi.io/oauth2-proxy@${ OAUTH2_PROXY_DIGEST } ` ) ;
26- expect ( workflow ) . not . toContain ( `dhi.io/oauth2-proxy:7.15.2-debian13@${ OAUTH2_PROXY_DIGEST } ` ) ;
27- expect ( workflow ) . toContain ( ` ghcr.io/terion-name/terrarium-dhi-oauth2-proxy:7.15.2-debian13` ) ;
44+ expect ( workflow ) . toContain ( `source: dhi.io/oauth2-proxy@${ OAUTH2_PROXY_DIGEST } ` ) ;
45+ expect ( workflow ) . not . toContain ( `source: dhi.io/oauth2-proxy:7.15.2-debian13@${ OAUTH2_PROXY_DIGEST } ` ) ;
46+ expect ( workflow ) . toContain ( "target: ghcr.io/terion-name/terrarium-dhi-oauth2-proxy:7.15.2-debian13" ) ;
2847 expect ( workflow ) . toContain ( `source_digest: ${ OAUTH2_PROXY_DIGEST } ` ) ;
29- expect ( workflow ) . toContain ( `target_digest: ${ OAUTH2_PROXY_MIRROR_DIGEST } ` ) ;
30- expect ( workflow ) . toContain ( `dhi.io/postgres@${ POSTGRES_DIGEST } ` ) ;
48+ expect ( workflow ) . toContain ( `source: dhi.io/postgres@${ POSTGRES_DIGEST } ` ) ;
3149 expect ( workflow ) . not . toContain ( "17.9-alpine3.22-fips" ) ;
32- expect ( workflow ) . toContain ( "target: ghcr.io/terion-name/terrarium-dhi-postgres:17.9-alpine3.22" ) ;
50+ expect ( workflow ) . not . toContain ( "target: ghcr.io/terion-name/terrarium-dhi-postgres:17.9-alpine3.22" ) ;
51+ expect ( workflow ) . toContain ( "target: ghcr.io/terion-name/terrarium-dhi-postgres:17.10-alpine3.22" ) ;
3352 expect ( workflow ) . toContain ( `source_digest: ${ POSTGRES_DIGEST } ` ) ;
34- expect ( workflow . match ( / t a r g e t _ d i g e s t : s h a 2 5 6 : d e 3 0 5 9 7 6 d 6 a 8 1 c 4 c 1 a d 2 6 0 8 6 1 e c 5 0 2 8 f a a f b b 1 b e 0 b c a 6 8 a b 3 7 9 e b 2 f b 6 2 1 a b e 3 4 / g) ) . toHaveLength ( 1 ) ;
53+ expect ( workflow . match ( / t a r g e t _ d i g e s t : a u t o / g) ) . toHaveLength ( 2 ) ;
3554 expect ( workflow . match ( / a r c h e s : a m d 6 4 , a r m 6 4 / g) ) . toHaveLength ( 2 ) ;
3655
3756 expect ( script ) . toContain ( "skopeo inspect --raw" ) ;
@@ -46,8 +65,75 @@ describe("Docker Hardened Image mirror workflow", () => {
4665 expect ( script ) . toContain ( '(.platform.architecture // "") == $arch' ) ;
4766 } ) ;
4867
68+ test ( "defines explicit GHCR package metadata for each mirrored DHI image" , ( ) => {
69+ const workflow = readWorkflow ( ) ;
70+ const descriptions = [ ...workflow . matchAll ( / d e s c r i p t i o n : " ( [ ^ " ] + ) " / g) ] . map ( ( match ) => match [ 1 ] ) ;
71+
72+ expect ( workflow ) . toContain ( `description: "${ OAUTH2_PROXY_DESCRIPTION } "` ) ;
73+ expect ( workflow ) . toContain ( `original_ref: dhi.io/oauth2-proxy:7.15.2-debian13@${ OAUTH2_PROXY_DIGEST } ` ) ;
74+ expect ( workflow ) . toContain ( "original_url: https://hub.docker.com/hardened-images/catalog/dhi/oauth2-proxy" ) ;
75+ expect ( workflow ) . toContain ( `source_repo_url: ${ SOURCE_REPO_URL } ` ) ;
76+ expect ( workflow ) . toContain ( `description: "${ POSTGRES_DESCRIPTION } "` ) ;
77+ expect ( workflow ) . toContain ( `original_ref: dhi.io/postgres:17.10-alpine3.22@${ POSTGRES_DIGEST } ` ) ;
78+ expect ( workflow ) . toContain ( "original_url: https://hub.docker.com/hardened-images/catalog/dhi/postgres" ) ;
79+ expect ( workflow . match ( new RegExp ( `source_repo_url: ${ SOURCE_REPO_URL } ` , "g" ) ) ) . toHaveLength ( 2 ) ;
80+
81+ expect ( descriptions ) . toHaveLength ( 2 ) ;
82+ for ( const description of descriptions ) {
83+ expect ( description . length ) . toBeLessThan ( 512 ) ;
84+ expect ( description ) . toContain ( "Mirror of Docker Hardened Image" ) ;
85+ expect ( description ) . toContain ( "for pulling without Docker authentication" ) ;
86+ expect ( description ) . toContain ( "Original: https://hub.docker.com/hardened-images/catalog/dhi/" ) ;
87+ }
88+ } ) ;
89+
90+ test ( "sets up Buildx and passes the metadata contract to the mirror helper" , ( ) => {
91+ const workflow = readWorkflow ( ) ;
92+ const invocation = workflow . match ( / \. g i t h u b \/ s c r i p t s \/ m i r r o r - d h i - i m a g e \. s h [ \s \S ] * ?\n \n - n a m e : M a k e G H C R p a c k a g e p u b l i c w h e n p e r m i t t e d / ) ?. [ 0 ] ?? "" ;
93+
94+ expect ( workflow ) . toContain ( "uses: docker/setup-buildx-action@v3" ) ;
95+ expect ( invocation . match ( / \$ \{ \{ m a t r i x \. / g) ) . toHaveLength ( 10 ) ;
96+ expect ( invocation ) . toContain ( '"${{ matrix.name }}"' ) ;
97+ expect ( invocation ) . toContain ( '"${{ matrix.source }}"' ) ;
98+ expect ( invocation ) . toContain ( '"${{ matrix.target }}"' ) ;
99+ expect ( invocation ) . toContain ( '"${{ matrix.source_digest }}"' ) ;
100+ expect ( invocation ) . toContain ( '"${{ matrix.target_digest }}"' ) ;
101+ expect ( invocation ) . toContain ( '"${{ matrix.arches }}"' ) ;
102+ expect ( invocation ) . toContain ( '"${{ matrix.description }}"' ) ;
103+ expect ( invocation ) . toContain ( '"${{ matrix.original_ref }}"' ) ;
104+ expect ( invocation ) . toContain ( '"${{ matrix.original_url }}"' ) ;
105+ expect ( invocation ) . toContain ( '"${{ matrix.source_repo_url }}"' ) ;
106+ } ) ;
107+
108+ test ( "annotates the final GHCR index and verifies annotation metadata" , ( ) => {
109+ const script = readScript ( ) ;
110+
111+ expect ( script ) . toContain ( 'if [ "$#" -ne 10 ]; then' ) ;
112+ expect ( script ) . toContain ( "<description> <original-ref> <original-url> <source-repo-url>" ) ;
113+ expect ( script ) . toContain ( "docker buildx imagetools create" ) ;
114+ expect ( script ) . toContain ( '--tag "${target_ref}"' ) ;
115+ expect ( script ) . toContain ( '"${target_ref}"' ) ;
116+ expect ( script ) . toContain ( "skopeo inspect --raw \"docker://${target_ref}\" >\"$target_raw\"" ) ;
117+ expect ( script ) . toContain ( "jq -e" ) ;
118+ for ( const key of requiredAnnotationKeys ) {
119+ expect ( script ) . toContain ( `index:${ key } ` ) ;
120+ expect ( script ) . toContain ( `.annotations["${ key } "]` ) ;
121+ }
122+ } ) ;
123+
124+ test ( "reports auto target digests without failing and writes a summary" , ( ) => {
125+ const script = readScript ( ) ;
126+
127+ expect ( script ) . toContain ( 'if [ "$expected_target_digest" != "auto" ]' ) ;
128+ expect ( script ) . toContain ( 'if [ "$expected_target_digest" = "auto" ]; then' ) ;
129+ expect ( script ) . toContain ( "::notice title=Final annotated GHCR digest::" ) ;
130+ expect ( script ) . toContain ( "GITHUB_STEP_SUMMARY" ) ;
131+ expect ( script ) . toContain ( "| Image | Tag | Final annotated digest |" ) ;
132+ expect ( script ) . toContain ( "| ${name} | ${target_ref} | ${target_digest} |" ) ;
133+ } ) ;
134+
49135 test ( "keeps the mirror helper executable for GitHub Actions" , ( ) => {
50- const mode = statSync ( join ( repoRoot , ".github/scripts/mirror-dhi-image.sh" ) ) . mode ;
136+ const mode = statSync ( scriptPath ) . mode ;
51137
52138 expect ( mode & 0o111 ) . not . toBe ( 0 ) ;
53139 } ) ;
0 commit comments