@@ -8,56 +8,81 @@ executors:
88 working_directory : /home/circleci/project
99
1010commands :
11- build -commands :
11+ start -commands :
1212 steps :
13- - checkout
14- - restore_cache :
15- keys :
16- - cache-v1-{{ .Revision }}-{{ .Environment.CIRCLE_WORKFLOW_ID }}
13+ - send-notification :
14+ message : " CI run started "
15+ - send-notification :
16+ message : " <${CIRCLE_BUILD_URL}|logs> "
1717 - run :
18- name : After cache restore
18+ name : Create compare link
1919 command : |
20- git clean -xdn
21- mkdir -p docker_cache
22- ls docker_cache
23- #sudo docker images
24- #if [ -f docker_cache/images.tar.gz ]; then gunzip -c docker_cache/images.tar.gz | sudo docker load; fi
20+ DEPLOYS_URL_API="https://api.github.com/repos/Farmbot/Farmbot-Web-App/deployments"
21+ COMPARE_URL_WEB="https://github.com/Farmbot/Farmbot-Web-App/compare/"
22+ data=$(curl -fsS "$DEPLOYS_URL_API") || data="[]"
23+ last_sha=$(printf "%s" "$data" | python -c 'import json,sys; data=json.loads(sys.stdin.read() or "[]"); deploy_index=0; sha=(data[deploy_index] or {}).get("sha"); print(sha or "")')
24+ compare_url="$COMPARE_URL_WEB$last_sha...${CIRCLE_SHA1}"
25+ echo "$compare_url"
26+ echo "export COMPARE_URL='$compare_url'" >> "$BASH_ENV"
27+ - send-notification :
28+ message : " <$COMPARE_URL|diff>"
29+ send-notification :
30+ parameters :
31+ message :
32+ type : string
33+ when :
34+ type : enum
35+ enum : ["on_success", "on_fail", "always"]
36+ default : " always"
37+ steps :
38+ - when :
39+ condition :
40+ equal : [staging, << pipeline.git.branch >>]
41+ steps :
42+ - run :
43+ name : " Send notification: << parameters.message >>"
44+ when : << parameters.when >>
45+ command : |
46+ if [ -n "$SLACK_WEBHOOK_URL" ]; then
47+ curl -fsS -X POST -H "Content-Type: application/json" \
48+ --data "{\"text\":\"<< parameters.message >>\",\"channel\":\"#software\"}" \
49+ "$SLACK_WEBHOOK_URL" || true
50+ fi
51+ build-commands :
52+ steps :
2553 - run :
2654 name : Build and Install Deps
2755 command : |
2856 mv .circleci/circle_envs .env
2957 echo -e '\ndocker_volumes/db/pg_wal/*' >> .dockerignore
3058 sudo docker compose build web
31- sudo docker compose run web gem install bundler
32- sudo docker compose run web bundle install
33- sudo docker compose run web npm install
34- sudo docker compose run web bundle exec rails db:create
35- sudo docker compose run web bundle exec rails db:migrate
36- sudo docker compose run web bundle exec rake keys:generate
37- - run :
38- name : After cache update
39- command : |
40- mkdir -p /tmp/test-results
41- git clean -xdn
42- ls docker_cache
43- #sudo docker images
44- #if [ ! -f docker_cache/images.tar.gz ]; then sudo docker save $(sudo docker images ruby -q) | gzip > docker_cache/images.tar.gz; fi
45- # - save_cache:
46- # key: cache-v1-{{ .Revision }}-{{ .Environment.CIRCLE_WORKFLOW_ID }}
47- # paths:
48- # - docker_volumes
49- # - node_modules
50- # - docker_cache
59+ if sudo docker compose run --rm web bash -lc '\
60+ gem install bundler && \
61+ bundle install && \
62+ bun install && \
63+ bundle exec rails db:create && \
64+ bundle exec rails db:migrate && \
65+ bundle exec rake keys:generate \
66+ '; then
67+ echo "export SETUP_OK=true" >> "$BASH_ENV"
68+ else
69+ echo "export SETUP_OK=false" >> "$BASH_ENV"
70+ exit 1
71+ fi
5172 rspec-commands :
5273 steps :
5374 - run :
5475 name : Run Ruby Tests
5576 command : |
56- sudo docker compose run web bundle exec rspec spec --format progress --format RspecJunitFormatter --out /tmp/ test-results/rspec /rspec.xml
77+ sudo docker compose run --rm web bundle exec rspec spec --format progress --format RspecJunitFormatter --out test-results/rspec.xml
5778 - run :
5879 name : Check app coverage status
5980 command : |
60- sudo docker compose run web bundle exec rake check_file_coverage:api || [ $CIRCLE_BRANCH == "staging" ]
81+ if [ "$SETUP_OK" != "true" ]; then
82+ echo "skipping"
83+ exit 0
84+ fi
85+ sudo docker compose run --rm web bundle exec rake check_file_coverage:api || [ $CIRCLE_BRANCH == "staging" ]
6186 when : always
6287 - run :
6388 name : Upload app coverage to Codecov
@@ -70,32 +95,59 @@ commands:
7095 shasum -a 256 -c codecov.SHA256SUM
7196 chmod +x codecov
7297 ./codecov -t $CODECOV_TOKEN -f coverage_api/coverage.xml
73- jest -commands :
98+ js-test -commands :
7499 steps :
75100 - run :
76101 name : Run JS tests
77102 command : |
78- sudo docker compose run web npm run test-slow -- -c .circleci/jest-ci.config.js -w 6
103+ sudo docker compose run --rm web bun test --reporter=junit --reporter-outfile=test-results/junit.xml
79104 echo 'export COVERAGE_AVAILABLE=true' >> $BASH_ENV
80105 lint-commands :
81106 steps :
82107 - run :
83108 name : Run JS Linters
84109 command : |
85- sudo docker compose run web npm run linters
110+ if [ "$SETUP_OK" != "true" ]; then
111+ echo "skipping"
112+ exit 0
113+ fi
114+ sudo docker compose run --rm web bun run linters
115+ when : always
116+ bun-build-commands :
117+ steps :
118+ - run :
119+ name : Check bun build
120+ command : |
121+ if [ "$SETUP_OK" != "true" ]; then
122+ echo "skipping"
123+ exit 0
124+ fi
125+ sudo docker compose run --rm web bundle exec rake assets:precompile 2>&1 | tee assets_precompile.log
126+ printf -- '-%.0s' {1..50}; printf '\n'
127+ if grep -Ei '^(warn|error):' assets_precompile.log; then
128+ exit 1
129+ fi
86130 when : always
87131 coverage-commands :
88132 steps :
89133 - run :
90134 name : Check frontend coverage status
91135 command : |
92- sudo docker compose run -e CIRCLE_SHA1="$CIRCLE_SHA1" -e CIRCLE_BRANCH="$CIRCLE_BRANCH" -e CIRCLE_PULL_REQUEST="$CIRCLE_PULL_REQUEST" web bundle exec rake coverage:run || [ $CIRCLE_BRANCH == "staging" ]
136+ if [ "$SETUP_OK" != "true" ]; then
137+ echo "skipping"
138+ exit 0
139+ fi
140+ sudo docker compose run --rm -e CIRCLE_SHA1="$CIRCLE_SHA1" -e CIRCLE_BRANCH="$CIRCLE_BRANCH" -e CIRCLE_PULL_REQUEST="$CIRCLE_PULL_REQUEST" web bundle exec rake coverage:run || [ $CIRCLE_BRANCH == "staging" ]
93141 when : always
94142 - run :
95143 name : Check frontend file coverage status
96144 command : |
145+ if [ "$SETUP_OK" != "true" ]; then
146+ echo "skipping"
147+ exit 0
148+ fi
97149 changed=$(git diff --name-only staging...HEAD | tr '\n' ',' | sed 's/,$//') || true
98- sudo docker compose run -e CHANGED_FILES="$changed" web bundle exec rake check_file_coverage:fe || true
150+ sudo docker compose run --rm - e CHANGED_FILES="$changed" web bundle exec rake check_file_coverage:fe || true
99151 when : always
100152 - run :
101153 name : Report frontend coverage to Coveralls
@@ -111,61 +163,176 @@ commands:
111163 fi
112164 if [ "$CIRCLE_BRANCH" == "staging" ]; then echo; fi
113165 when : always # change to `on_success` for a stricter comparison
166+ render-commands :
167+ steps :
168+ - run :
169+ name : Start services
170+ when : always
171+ command : |
172+ if [ "$SETUP_OK" != "true" ]; then
173+ echo "skipping"
174+ exit 0
175+ fi
176+ sudo docker compose up -d
177+ sudo docker compose ps
178+ - run :
179+ name : Install playwright
180+ when : always
181+ command : |
182+ if [ "$SETUP_OK" != "true" ]; then
183+ echo "skipping"
184+ exit 0
185+ fi
186+ sudo docker compose exec -e PLAYWRIGHT_BROWSERS_PATH=0 web bunx playwright install chromium --with-deps
187+ - run :
188+ name : Wait for load
189+ when : always
190+ command : |
191+ if [ "$SETUP_OK" != "true" ]; then
192+ echo "skipping"
193+ exit 0
194+ fi
195+ for i in $(seq 1 90); do
196+ if curl -fsS "http://127.0.0.1:3000/promo" >/dev/null; then
197+ echo "web is up"
198+ exit 0
199+ fi
200+ sleep 2
201+ done
202+
203+ echo "timeout"
204+ sudo docker compose logs --no-color --tail=300 web
205+ exit 1
206+ - run :
207+ name : Run playwright
208+ when : always
209+ command : |
210+ if [ "$SETUP_OK" != "true" ]; then
211+ echo "skipping"
212+ exit 0
213+ fi
214+ attempts=2
215+ for _ in $(seq 1 "$attempts"); do
216+ url="http://localhost:3000/promo?sizePreset=Genesis+XL&promoSpread=true"
217+ echo "Attempting FPS check via ${url}"
218+ if ! sudo docker compose exec web curl -I "${url}" >/dev/null; then
219+ continue
220+ fi
221+
222+ if ! fps_output=$(sudo docker compose exec -e PLAYWRIGHT_BROWSERS_PATH=0 web bun scripts/fps.js "${url}" "tmp/promo.png"); then
223+ echo "${fps_output}"
224+ continue
225+ fi
226+ echo "${fps_output}"
227+
228+ fps_value=$(echo "${fps_output}" | awk -F= '/^FPS_VALUE=/{print $2; exit}')
229+ scene_metrics=$(echo "${fps_output}" | awk -F= '/^SCENE_METRICS=/{print $2; exit}')
230+ echo "export FPS_VALUE=${fps_value}" >> "$BASH_ENV"
231+ echo "export SCENE_METRICS=\"${scene_metrics}\"" >> "$BASH_ENV"
232+
233+ exit 0
234+ done
235+
236+ echo "FPS check failed for all URLs"
237+ exit 1
238+ - restore_cache :
239+ keys :
240+ - fps_value-staging-
241+ - run :
242+ name : Load previous value
243+ when : always
244+ command : |
245+ if [ -f fps_value.txt ]; then
246+ PREV=$(cat fps_value.txt)
247+ else
248+ PREV=0.75
249+ fi
250+ echo "Previous value: $PREV fps"
251+ echo "export PREV_VALUE=$PREV" >> $BASH_ENV
252+ - run :
253+ name : Compare value
254+ when : always
255+ command : |
256+ percent_change=$(python -c 'import os; fps=float(os.environ.get("FPS_VALUE","0") or 0); prev=float(os.environ.get("PREV_VALUE","0") or 0); print("n/a" if prev==0 else f"{((fps-prev)/prev)*100:.2f}")')
257+ echo "$FPS_VALUE fps ($percent_change% change)"
258+ echo "export PERCENT_CHANGE=$percent_change" >> "$BASH_ENV"
259+ - run :
260+ name : Save new value
261+ command : |
262+ echo "$FPS_VALUE"
263+ echo "$FPS_VALUE" > fps_value.txt
264+ if [ ! -f scene_metrics.csv ]; then
265+ printf '%s\n' "epoch, FPS, Calls, Triangles, Points, Lines, Geometries, Textures, Objects, Meshes, Instanced meshes" > scene_metrics.csv
266+ fi
267+ printf '%s\n' "$SCENE_METRICS" >> scene_metrics.csv
268+ cat scene_metrics.csv
269+ - save_cache :
270+ key : fps_value-{{ .Branch }}-{{ epoch }}
271+ paths :
272+ - fps_value.txt
273+ - scene_metrics.csv
274+ - send-notification :
275+ message : " $FPS_VALUE fps (${PERCENT_CHANGE}% change)"
276+ - run :
277+ name : On failure
278+ when : on_fail
279+ command : |
280+ if [ "$SETUP_OK" != "true" ]; then
281+ echo "setup failed"
282+ exit 0
283+ fi
284+ sudo docker compose ps
285+ sudo docker compose logs --no-color --tail=500
286+ - store_artifacts :
287+ path : tmp/promo.png
288+ destination : promo_xl.png
289+ - store_artifacts :
290+ path : scene_metrics.csv
291+ destination : scene_metrics.csv
292+ end-commands :
293+ steps :
294+ - run :
295+ name : Fetch first artifact URL
296+ command : |
297+ project_slug="gh/${CIRCLE_PROJECT_USERNAME}/${CIRCLE_PROJECT_REPONAME}"
298+ artifacts_json=$(curl -fsS \
299+ -H "Accept: application/json" \
300+ "https://circleci.com/api/v2/project/${project_slug}/${CIRCLE_BUILD_NUM}/artifacts")
301+ artifact_url=$(printf "%s" "$artifacts_json" | python -c 'import json,sys; data=json.load(sys.stdin); items=data.get("items") or []; print(items[0].get("url","") if items else "")')
302+ echo "export ARTIFACT_URL=$artifact_url" >> "$BASH_ENV"
303+ - send-notification :
304+ message : " CI run succeeded"
305+ when : on_success
306+ - send-notification :
307+ message : " <$ARTIFACT_URL|screenshot>"
308+ when : on_success
309+ - send-notification :
310+ message : " CI run failed"
311+ when : on_fail
312+
114313
115314
116315workflows :
117316 version : 2
118317 build_and_test :
119- max_auto_reruns : 1
318+ # max_auto_reruns: 1
120319 jobs :
121- # - build
122320 - all
123- # - test-api:
124- # requires:
125- # - build
126- # - run-linters:
127- # requires:
128- # - build
129- # - test-fe:
130- # requires:
131- # - build
132321
133322jobs :
134- build :
135- executor : build-executor
136- steps :
137- - build-commands
138323 all :
139324 executor : build-executor
140325 steps :
326+ - start-commands
327+ - checkout
328+ - run : mkdir -p test-results
141329 - build-commands
142330 - rspec-commands
143331 - lint-commands
144- - jest-commands
332+ - bun-build-commands
333+ - js-test-commands
145334 - store_test_results :
146- path : /tmp/ test-results
335+ path : test-results
147336 - coverage-commands
148- test-api :
149- executor : build-executor
150- steps :
151- - build-commands
152- - rspec-commands
153- - store_test_results :
154- path : /tmp/test-results
155- run-linters :
156- executor : build-executor
157- steps :
158- - build-commands
159- - lint-commands
160- test-fe :
161- executor : build-executor
162- parallelism : 4
163- steps :
164- - build-commands
165- - run :
166- name : Run JS Tests
167- command : |
168- circleci tests glob **/__tests__/**/*.ts* | circleci tests split > /tmp/tests-to-run
169- sudo docker compose run web npm run test-very-slow -- -c .circleci/jest-ci.config.js $(cat /tmp/tests-to-run)
170- - store_test_results :
171- path : /tmp/test-results
337+ - render-commands
338+ - end-commands
0 commit comments