@@ -18,60 +18,12 @@ jobs:
1818 - name : Checkout repository
1919 uses : actions/checkout@v4
2020
21- - name : Generate .env from secrets
22- env :
23- RELOAD_PASSWORD : ${{ secrets.RELOAD_PASSWORD }}
24- CFCONFIG_ADMIN_PASSWORD : ${{ secrets.CFCONFIG_ADMIN_PASSWORD }}
25- WHEELSDEV_HOST : ${{ secrets.WHEELSDEV_HOST }}
26- WHEELSDEV_PORT : ${{ secrets.WHEELSDEV_PORT }}
27- WHEELSDEV_DATABASENAME : ${{ secrets.WHEELSDEV_DATABASENAME }}
28- WHEELSDEV_USERNAME : ${{ secrets.WHEELSDEV_USERNAME }}
29- WHEELSDEV_PASSWORD : ${{ secrets.WHEELSDEV_PASSWORD }}
30- SMTP_USERNAME : ${{ secrets.SMTP_USERNAME }}
31- SMTP_PASSWORD : ${{ secrets.SMTP_PASSWORD }}
32- WHEELS_ID_SALT : ${{ secrets.WHEELS_ID_SALT }}
33- SENTRY_DSN : ${{ secrets.SENTRY_DSN }}
21+ - name : Resolve base image digest for reproducible builds
3422 run : |
35- cat > .env << ENVEOF
36- LUCEE_EXTENSIONS=BEC20D47-3268-1B354-C0E8E70B5CBC15A1;name=PostgreSQL;version=42.7.4
37-
38- environment=production
39- reloadPassword=${RELOAD_PASSWORD}
40-
41- cfconfig_adminPassword=${CFCONFIG_ADMIN_PASSWORD}
42-
43- application_host=https://wheels.dev
44- datasource=wheels.dev
45-
46- wheelsdev_host=${WHEELSDEV_HOST}
47- wheelsdev_port=${WHEELSDEV_PORT}
48- wheelsdev_databasename=${WHEELSDEV_DATABASENAME}
49- wheelsdev_username=${WHEELSDEV_USERNAME}
50- wheelsdev_password=${WHEELSDEV_PASSWORD}
51- wheelsdev_clob=true
52- wheelsdev_connectionlimit=100
53- wheelsdev_storage=true
54-
55- wheelsdev_storage=true
56- sessionStorage=wheels.dev
57- sessionCluster=true
58-
59- test_case=false
60- mail_from=noreply@wheels.dev
61-
62- smtp_host=smtp.postmarkapp.com
63- smtp_port=587
64- smtp_username=${SMTP_USERNAME}
65- smtp_password=${SMTP_PASSWORD}
66- smtp_ssl=false
67- smtp_tls=true
68-
69- wheels_id_salt=${WHEELS_ID_SALT}
70-
71- SENTRY_DSN=${SENTRY_DSN}
72- SENTRY_ENVIRONMENT=production
73- ENVEOF
74- sed -i 's/^ //' .env
23+ docker pull ortussolutions/commandbox:lucee6
24+ DIGEST=$(docker inspect --format='{{index .RepoDigests 0}}' ortussolutions/commandbox:lucee6)
25+ echo "COMMANDBOX_DIGEST=${DIGEST}" >> "$GITHUB_ENV"
26+ echo "Pinned base image: ${DIGEST}"
7527
7628 - name : Copy swarm Dockerfile to root
7729 run : |
10355 context : .
10456 file : ./dockerfile
10557 push : true
58+ build-args : |
59+ COMMANDBOX_IMAGE=${{ env.COMMANDBOX_DIGEST }}
10660 tags : |
107- ${{ env.IMAGE_NAME }}:latest
10861 ${{ env.IMAGE_NAME }}:${{ github.sha }}
10962 cache-from : type=gha
11063 cache-to : type=gha,mode=max
@@ -128,31 +81,50 @@ jobs:
12881 - name : Set task history limit
12982 run : docker swarm update --task-history-limit 2
13083
84+ - name : Resolve compose file with secrets
85+ env :
86+ IMAGE_TAG : ${{ github.sha }}
87+ RELOAD_PASSWORD : ${{ secrets.RELOAD_PASSWORD }}
88+ CFCONFIG_ADMIN_PASSWORD : ${{ secrets.CFCONFIG_ADMIN_PASSWORD }}
89+ WHEELSDEV_HOST : ${{ secrets.WHEELSDEV_HOST }}
90+ WHEELSDEV_PORT : ${{ secrets.WHEELSDEV_PORT }}
91+ WHEELSDEV_DATABASENAME : ${{ secrets.WHEELSDEV_DATABASENAME }}
92+ WHEELSDEV_USERNAME : ${{ secrets.WHEELSDEV_USERNAME }}
93+ WHEELSDEV_PASSWORD : ${{ secrets.WHEELSDEV_PASSWORD }}
94+ SMTP_USERNAME : ${{ secrets.SMTP_USERNAME }}
95+ SMTP_PASSWORD : ${{ secrets.SMTP_PASSWORD }}
96+ WHEELS_ID_SALT : ${{ secrets.WHEELS_ID_SALT }}
97+ SENTRY_DSN : ${{ secrets.SENTRY_DSN }}
98+ run : |
99+ envsubst '$IMAGE_TAG $RELOAD_PASSWORD $CFCONFIG_ADMIN_PASSWORD $WHEELSDEV_HOST $WHEELSDEV_PORT $WHEELSDEV_DATABASENAME $WHEELSDEV_USERNAME $WHEELSDEV_PASSWORD $SMTP_USERNAME $SMTP_PASSWORD $WHEELS_ID_SALT $SENTRY_DSN' \
100+ < deploy/swarm/docker-compose.yml \
101+ > /tmp/docker-compose-resolved.yml
102+
131103 - name : Deploy stack via Portainer API
132104 env :
133105 PORTAINER_URL : ${{ secrets.PORTAINER_URL }}
134106 PORTAINER_API_KEY : ${{ secrets.PORTAINER_API_KEY }}
135107 PORTAINER_ENDPOINT_ID : ${{ secrets.PORTAINER_ENDPOINT_ID }}
136108 run : |
137- COMPOSE_CONTENT=$(cat deploy/swarm /docker-compose.yml)
109+ COMPOSE_CONTENT=$(cat /tmp /docker-compose-resolved .yml)
138110
139111 # Get the swarm ID from Portainer
140- SWARM_ID=$(curl -sfk \
112+ SWARM_ID=$(curl -sf \
141113 -H "X-API-Key: ${PORTAINER_API_KEY}" \
142114 "${PORTAINER_URL}/api/endpoints/${PORTAINER_ENDPOINT_ID}/docker/swarm" \
143115 | jq -r '.ID')
144116 echo "Swarm ID: ${SWARM_ID}"
145117
146118 # Check if the stack already exists
147- STACK_INFO=$(curl -sfk \
119+ STACK_INFO=$(curl -sf \
148120 -H "X-API-Key: ${PORTAINER_API_KEY}" \
149121 "${PORTAINER_URL}/api/stacks" \
150122 | jq -r '.[] | select(.Name == "wheels-dev")')
151123
152124 if [ -n "$STACK_INFO" ]; then
153125 STACK_ID=$(echo "$STACK_INFO" | jq -r '.Id')
154126 echo "Updating existing stack (ID: ${STACK_ID})..."
155- HTTP_CODE=$(curl -sk -o /tmp/portainer-response.json -w "%{http_code}" \
127+ HTTP_CODE=$(curl -s -o /tmp/portainer-response.json -w "%{http_code}" \
156128 -X PUT \
157129 -H "X-API-Key: ${PORTAINER_API_KEY}" \
158130 -H "Content-Type: application/json" \
@@ -163,7 +135,7 @@ jobs:
163135 }')")
164136 else
165137 echo "Creating new stack..."
166- HTTP_CODE=$(curl -sk -o /tmp/portainer-response.json -w "%{http_code}" \
138+ HTTP_CODE=$(curl -s -o /tmp/portainer-response.json -w "%{http_code}" \
167139 -X POST \
168140 -H "X-API-Key: ${PORTAINER_API_KEY}" \
169141 -H "Content-Type: application/json" \
@@ -183,12 +155,14 @@ jobs:
183155 fi
184156 echo "Stack deployed successfully via Portainer"
185157
186- - name : Force pull latest image
158+ - name : Update service to SHA-tagged image
159+ env :
160+ IMAGE_TAG : ${{ github.sha }}
187161 run : |
188162 for i in 1 2 3 4 5; do
189163 docker service update \
190164 --with-registry-auth \
191- --image ghcr.io/wheels-dev/wheels-dev:latest \
165+ --image " ghcr.io/wheels-dev/wheels-dev:${IMAGE_TAG}" \
192166 --force \
193167 wheels-dev_wheels-dev && break
194168 echo "Attempt $i failed (update out of sequence), retrying in 15s..."
0 commit comments