Compare commits

..
11 Commits
Author SHA1 Message Date
Ryan ef4db213c7 test: testing ci/cd
Build & Deploy (develop) / build-and-deploy (push) Successful in 1m40s
Runner Test / hello (push) Successful in 4s
2026-09-07 08:33:08 +03:30
Ryan 9ba09fb55b fix:changed hleath check logic
Build & Deploy (develop) / build-and-deploy (push) Successful in 10s
2026-09-07 08:27:12 +03:30
Ryan 4b4e31a55f fix:increased health check retires on workflow
Build & Deploy (develop) / build-and-deploy (push) Failing after 3m2s
2026-09-07 08:20:44 +03:30
Hamed 9b2ae99df6 Merge branch 'develop' of ssh://git.my-rm.com:2222/Robot-Market/static-front into develop
Build & Deploy (develop) / build-and-deploy (push) Failing after 39s
feat:workflows fetched
2026-09-07 08:05:50 +03:30
Ryan af9f92478e feat:added CI/CD worflow and rollback workflow
Build & Deploy (develop) / build-and-deploy (push) Failing after 1m47s
2026-09-06 14:22:05 +03:30
Ryan affb1b1ad8 feat:added CI/CD worflow and rollback workflow
Build & Deploy (develop) / build-and-deploy (push) Failing after 6s
2026-09-06 13:52:34 +03:30
Hamed a8d46921f9 initial new branch 2026-09-06 13:37:59 +03:30
Hamed 5eee968d1c initial new branch 2026-09-06 12:18:08 +03:30
Hamed dcee392e02 initial new branch 2026-09-06 11:47:56 +03:30
Hamed 8803dfa49a initial new branch 2026-09-06 11:47:52 +03:30
Hamed 52a3bb9ab7 feat:dockerized & test actions added 2026-09-06 09:55:45 +03:30
7 changed files with 201 additions and 48 deletions
+85
View File
@@ -0,0 +1,85 @@
name: Build & Deploy (develop)
# Runs on:
# - Pull requests targeting develop
# - Direct pushes to develop
# main is intentionally NOT included yet (no prod target configured)
on:
push:
branches:
- develop
pull_request:
branches:
- develop
jobs:
build-and-deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Resolve commit SHA
id: vars
run: |
# For PRs, gitea.sha is a temporary merge commit — use the real
# PR head commit instead so tags map to actual git history.
FULL_SHA="${{ gitea.event.pull_request.head.sha || gitea.sha }}"
echo "sha=${FULL_SHA:0:7}" >> "$GITHUB_OUTPUT"
- name: Build Docker image (tagged by commit SHA)
run: |
docker build -t robot-market:${{ steps.vars.outputs.sha }} .
- name: Save previous tag for rollback
run: |
if [ -f /opt/robot-market-front/.env ]; then
cp /opt/robot-market-front/.env /opt/robot-market-front/.env.previous
fi
- name: Point compose at this build
run: |
echo "IMAGE_TAG=${{ steps.vars.outputs.sha }}" > /opt/robot-market-front/.env
- name: Deploy via compose
run: |
docker compose --env-file /opt/robot-market-front/.env \
-f /opt/robot-market-front/docker-compose.yml \
up -d --force-recreate
- name: Health check (retries for ~30s)
run: |
for i in $(seq 1 15); do
if docker exec robot-market node -e "
require('http').get('http://localhost:3000', res => {
process.exit(res.statusCode < 400 ? 0 : 1);
}).on('error', () => process.exit(1));
" 2>/dev/null; then
echo "Container is healthy"
exit 0
fi
echo "Not ready yet, retrying..."
sleep 2
done
echo "Health check failed after 30s"
exit 1
- name: Roll back to previous version on failed health check
if: failure()
run: |
if [ -f /opt/robot-market-front/.env.previous ]; then
cp /opt/robot-market-front/.env.previous /opt/robot-market-front/.env
docker compose --env-file /opt/robot-market-front/.env \
-f /opt/robot-market-front/docker-compose.yml \
up -d --force-recreate
echo "Rolled back to previous image."
else
echo "No previous deploy recorded — nothing to roll back to."
fi
- name: Clean up dangling (untagged) layers only
run: docker image prune -f
# Note: this does NOT remove SHA-tagged images, so rollback targets
# stay available. Periodically clean up old SHA tags manually if
# disk usage grows (e.g. keep the last 10 builds).
+37
View File
@@ -0,0 +1,37 @@
name: Rollback (develop)
# Trigger manually from the Gitea Actions UI.
# Leave "sha" empty to roll back to the previous deploy.
# Fill "sha" in (short 7-char form, e.g. a3f9c21) to roll back to a specific past build.
on:
workflow_dispatch:
inputs:
sha:
description: "Short commit SHA to roll back to (empty = previous deploy)"
required: false
default: ""
jobs:
rollback:
runs-on: ubuntu-latest
steps:
- name: Determine target tag
run: |
if [ -n "${{ gitea.event.inputs.sha }}" ]; then
TAG="${{ gitea.event.inputs.sha }}"
else
TAG=$(grep IMAGE_TAG /opt/robot-market-front/.env.previous | cut -d= -f2)
fi
echo "Rolling back to: $TAG"
echo "IMAGE_TAG=$TAG" > /opt/robot-market-front/.env
- name: Verify that image actually exists locally
run: |
TAG=$(grep IMAGE_TAG /opt/robot-market-front/.env | cut -d= -f2)
docker image inspect robot-market:$TAG > /dev/null
- name: Redeploy target image
run: |
docker compose --env-file /opt/robot-market-front/.env \
-f /opt/robot-market-front/docker-compose.yml \
up -d --force-recreate
+64 -35
View File
@@ -4,13 +4,9 @@
<option name="autoReloadType" value="SELECTIVE" /> <option name="autoReloadType" value="SELECTIVE" />
</component> </component>
<component name="ChangeListManager"> <component name="ChangeListManager">
<list default="true" id="bb7b50a5-be9c-4cd7-a05b-979f0e91b173" name="Changes" comment="update"> <list default="true" id="bb7b50a5-be9c-4cd7-a05b-979f0e91b173" name="Changes" comment="initial new branch">
<change afterPath="$PROJECT_DIR$/.dockerignore" afterDir="false" />
<change afterPath="$PROJECT_DIR$/.gitea/workflows/test.yml" afterDir="false" />
<change afterPath="$PROJECT_DIR$/Dockerfile" afterDir="false" />
<change afterPath="$PROJECT_DIR$/docker-compose.yml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" /> <change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/next.config.js" beforeDir="false" afterPath="$PROJECT_DIR$/next.config.js" afterDir="false" /> <change beforePath="$PROJECT_DIR$/data/content.js" beforeDir="false" afterPath="$PROJECT_DIR$/data/content.js" afterDir="false" />
</list> </list>
<option name="SHOW_DIALOG" value="false" /> <option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" /> <option name="HIGHLIGHT_CONFLICTS" value="true" />
@@ -18,6 +14,11 @@
<option name="LAST_RESOLUTION" value="IGNORE" /> <option name="LAST_RESOLUTION" value="IGNORE" />
</component> </component>
<component name="Git.Settings"> <component name="Git.Settings">
<option name="RECENT_BRANCH_BY_REPOSITORY">
<map>
<entry key="$PROJECT_DIR$" value="develop" />
</map>
</option>
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" /> <option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
</component> </component>
<component name="JsbtTreeLayoutManager"> <component name="JsbtTreeLayoutManager">
@@ -93,33 +94,33 @@
<option name="hideEmptyMiddlePackages" value="true" /> <option name="hideEmptyMiddlePackages" value="true" />
<option name="showLibraryContents" value="true" /> <option name="showLibraryContents" value="true" />
</component> </component>
<component name="PropertiesComponent"><![CDATA[{ <component name="PropertiesComponent">{
"keyToString": { &quot;keyToString&quot;: {
"ASKED_MARK_IGNORED_FILES_AS_EXCLUDED": "true", &quot;ASKED_MARK_IGNORED_FILES_AS_EXCLUDED&quot;: &quot;true&quot;,
"ModuleVcsDetector.initialDetectionPerformed": "true", &quot;ModuleVcsDetector.initialDetectionPerformed&quot;: &quot;true&quot;,
"RunOnceActivity.ShowReadmeOnStart": "true", &quot;RunOnceActivity.ShowReadmeOnStart&quot;: &quot;true&quot;,
"RunOnceActivity.TerminalTabsStorage.copyFrom.TerminalArrangementManager.252": "true", &quot;RunOnceActivity.TerminalTabsStorage.copyFrom.TerminalArrangementManager.252&quot;: &quot;true&quot;,
"RunOnceActivity.git.unshallow": "true", &quot;RunOnceActivity.git.unshallow&quot;: &quot;true&quot;,
"RunOnceActivity.typescript.service.memoryLimit.init": "true", &quot;RunOnceActivity.typescript.service.memoryLimit.init&quot;: &quot;true&quot;,
"codeWithMe.voiceChat.enabledByDefault": "false", &quot;codeWithMe.voiceChat.enabledByDefault&quot;: &quot;false&quot;,
"git-widget-placeholder": "main", &quot;git-widget-placeholder&quot;: &quot;develop&quot;,
"javascript.nodejs.core.library.configured.version": "24.20.0", &quot;javascript.nodejs.core.library.configured.version&quot;: &quot;24.20.0&quot;,
"javascript.nodejs.core.library.typings.version": "24.13.3", &quot;javascript.nodejs.core.library.typings.version&quot;: &quot;24.13.3&quot;,
"javascript.preferred.runtime.type.id": "node", &quot;javascript.preferred.runtime.type.id&quot;: &quot;node&quot;,
"js.debugger.nextJs.config.created.client": "true", &quot;js.debugger.nextJs.config.created.client&quot;: &quot;true&quot;,
"js.debugger.nextJs.config.created.server": "true", &quot;js.debugger.nextJs.config.created.server&quot;: &quot;true&quot;,
"kronk.content.tree.expanded.paths": "", &quot;kronk.content.tree.expanded.paths&quot;: &quot;&quot;,
"last_opened_file_path": "/home/hamed/Documents/Projects/GItLabRepo/device_web", &quot;last_opened_file_path&quot;: &quot;/home/hamed/Documents/Projects/GItLabRepo/device_web&quot;,
"node.js.detected.package.eslint": "true", &quot;node.js.detected.package.eslint&quot;: &quot;true&quot;,
"node.js.detected.package.tslint": "true", &quot;node.js.detected.package.tslint&quot;: &quot;true&quot;,
"node.js.selected.package.eslint": "(autodetect)", &quot;node.js.selected.package.eslint&quot;: &quot;(autodetect)&quot;,
"node.js.selected.package.tslint": "(autodetect)", &quot;node.js.selected.package.tslint&quot;: &quot;(autodetect)&quot;,
"nodejs_package_manager_path": "npm", &quot;nodejs_package_manager_path&quot;: &quot;npm&quot;,
"settings.editor.selected.configurable": "editor.preferences.fonts.default", &quot;settings.editor.selected.configurable&quot;: &quot;editor.preferences.fonts.default&quot;,
"ts.external.directory.path": "C:\\Program Files\\JetBrains\\WebStorm 2026.1.1\\plugins\\javascript-plugin\\jsLanguageServicesImpl\\external", &quot;ts.external.directory.path&quot;: &quot;C:\\Program Files\\JetBrains\\WebStorm 2026.1.1\\plugins\\javascript-plugin\\jsLanguageServicesImpl\\external&quot;,
"vue.rearranger.settings.migration": "true" &quot;vue.rearranger.settings.migration&quot;: &quot;true&quot;
} }
}]]></component> }</component>
<component name="RecentsManager"> <component name="RecentsManager">
<key name="MoveFile.RECENT_KEYS"> <key name="MoveFile.RECENT_KEYS">
<recent name="D:\Project\GitHubRepo\robot-market-landing\public\images\icons" /> <recent name="D:\Project\GitHubRepo\robot-market-landing\public\images\icons" />
@@ -185,7 +186,8 @@
<workItem from="1788423298952" duration="3393000" /> <workItem from="1788423298952" duration="3393000" />
<workItem from="1788580635152" duration="776000" /> <workItem from="1788580635152" duration="776000" />
<workItem from="1788592387411" duration="8000" /> <workItem from="1788592387411" duration="8000" />
<workItem from="1788592424522" duration="8370000" /> <workItem from="1788592424522" duration="13793000" />
<workItem from="1788670304118" duration="1889000" />
</task> </task>
<task id="LOCAL-00001" summary="update"> <task id="LOCAL-00001" summary="update">
<option name="closed" value="true" /> <option name="closed" value="true" />
@@ -387,7 +389,31 @@
<option name="project" value="LOCAL" /> <option name="project" value="LOCAL" />
<updated>1788418070013</updated> <updated>1788418070013</updated>
</task> </task>
<option name="localTasksCounter" value="26" /> <task id="LOCAL-00026" summary="feat:dockerized &amp; test actions add">
<option name="closed" value="true" />
<created>1788601868240</created>
<option name="number" value="00026" />
<option name="presentableId" value="LOCAL-00026" />
<option name="project" value="LOCAL" />
<updated>1788601868240</updated>
</task>
<task id="LOCAL-00027" summary="initial new branch">
<option name="closed" value="true" />
<created>1788682674027</created>
<option name="number" value="00027" />
<option name="presentableId" value="LOCAL-00027" />
<option name="project" value="LOCAL" />
<updated>1788682674027</updated>
</task>
<task id="LOCAL-00028" summary="initial new branch">
<option name="closed" value="true" />
<created>1788682678051</created>
<option name="number" value="00028" />
<option name="presentableId" value="LOCAL-00028" />
<option name="project" value="LOCAL" />
<updated>1788682678051</updated>
</task>
<option name="localTasksCounter" value="29" />
<servers /> <servers />
</component> </component>
<component name="TypeScriptGeneratedFilesManager"> <component name="TypeScriptGeneratedFilesManager">
@@ -399,6 +425,9 @@
<component name="VcsManagerConfiguration"> <component name="VcsManagerConfiguration">
<MESSAGE value="ui : change the position of header in Hero Section" /> <MESSAGE value="ui : change the position of header in Hero Section" />
<MESSAGE value="update" /> <MESSAGE value="update" />
<option name="LAST_COMMIT_MESSAGE" value="update" /> <MESSAGE value="feat:dockerized &amp; test actions add" />
<MESSAGE value="feat:dockerized &amp; test actions added" />
<MESSAGE value="initial new branch" />
<option name="LAST_COMMIT_MESSAGE" value="initial new branch" />
</component> </component>
</project> </project>
+1
View File
@@ -26,4 +26,5 @@ COPY --from=builder /app/.next/static ./.next/static
EXPOSE 3000 EXPOSE 3000
CMD ["node", "server.js"] CMD ["node", "server.js"]
+1 -1
View File
@@ -47,7 +47,7 @@ export default function CartDrawer() {
// api() <===> todo : use this func to fetch ... // api() <===> todo : use this func to fetch ...
// ------------------------------------------------------------------- // -------------------------------------------------------------------
message.info(t.cart.checkoutToast); await message.info(t.cart.checkoutToast);
}; };
return ( return (
+1
View File
@@ -28,6 +28,7 @@ export default function CatalogSection() {
image: item.image, image: item.image,
}) })
); );
message.success(t.catalog.addToast(item.name)); message.success(t.catalog.addToast(item.name));
}; };
+12 -12
View File
@@ -9,8 +9,8 @@ const machinesFa = [
id: 'rm-35', id: 'rm-35',
name: 'روبات مارکت ۳۵ کانال', name: 'روبات مارکت ۳۵ کانال',
shortLabel: '۳۵ کانال', shortLabel: '۳۵ کانال',
priceLabel: '۳,۸۶۰,۰۰۰,۰۰۰ ریال', priceLabel: '4,200,000,000 ریال',
priceNumeric: 3860000000, priceNumeric: 4200000000,
description: 'یخچالدار، ۳۵ کانال اختصاصی، سیستم خنک‌کننده قدرتمند، تحویل نرم، مدیریت آنلاین ۲۴ ساعته.', description: 'یخچالدار، ۳۵ کانال اختصاصی، سیستم خنک‌کننده قدرتمند، تحویل نرم، مدیریت آنلاین ۲۴ ساعته.',
highlights: ['یخچالدار', '۳۵ کانال اختصاصی', 'تحویل نرم', 'مدیریت آنلاین ۲۴ ساعته'], highlights: ['یخچالدار', '۳۵ کانال اختصاصی', 'تحویل نرم', 'مدیریت آنلاین ۲۴ ساعته'],
image: '/images/machine-35-base.webp', image: '/images/machine-35-base.webp',
@@ -21,8 +21,8 @@ const machinesFa = [
id: 'rm-48', id: 'rm-48',
name: 'روبات مارکت ۴۸ کانال', name: 'روبات مارکت ۴۸ کانال',
shortLabel: '۴۸ کانال', shortLabel: '۴۸ کانال',
priceLabel: '۴,۴۸۰,۰۰۰,۰۰۰ ریال', priceLabel: '4,980,000,000 ریال',
priceNumeric: 4480000000, priceNumeric: 4980000000,
description: 'یخچالدار، ۴۸ کانال اختصاصی، قابلیت‌های پیشرفته مدیریت آنلاین و تحویل کالا.', description: 'یخچالدار، ۴۸ کانال اختصاصی، قابلیت‌های پیشرفته مدیریت آنلاین و تحویل کالا.',
highlights: ['یخچالدار', '۴۸ کانال اختصاصی', 'مدیریت آنلاین پیشرفته', 'تحویل هوشمند کالا'], highlights: ['یخچالدار', '۴۸ کانال اختصاصی', 'مدیریت آنلاین پیشرفته', 'تحویل هوشمند کالا'],
image: '/images/machine-48-base.webp', image: '/images/machine-48-base.webp',
@@ -33,8 +33,8 @@ const machinesFa = [
id: 'rm-60', id: 'rm-60',
name: 'روبات مارکت ۶۰ کانال', name: 'روبات مارکت ۶۰ کانال',
shortLabel: '۶۰ کانال', shortLabel: '۶۰ کانال',
priceLabel: '۴,۹۸۰,۰۰۰,۰۰۰ ریال', priceLabel: '5,480,000,000 ریال',
priceNumeric: 4980000000, priceNumeric: 5480000000,
description: 'یخچالدار، ۶۰ کانال اختصاصی برای کسب‌وکارهای پرتردد.', description: 'یخچالدار، ۶۰ کانال اختصاصی برای کسب‌وکارهای پرتردد.',
highlights: ['یخچالدار', '۶۰ کانال اختصاصی', 'مناسب اماکن پرتردد', 'ظرفیت بالا'], highlights: ['یخچالدار', '۶۰ کانال اختصاصی', 'مناسب اماکن پرتردد', 'ظرفیت بالا'],
image: '/images/machine-60-base.webp', image: '/images/machine-60-base.webp',
@@ -48,8 +48,8 @@ const machinesEn = [
id: 'rm-35', id: 'rm-35',
name: 'Robot Market RM-35', name: 'Robot Market RM-35',
shortLabel: '35 Channels', shortLabel: '35 Channels',
priceLabel: '3,860,000,000 Rials', priceLabel: '4,180,000,000 Rials',
priceNumeric: 3860000000, priceNumeric: 4180000000,
description: 'Refrigerated, 35 dedicated channels, powerful cooling system, soft delivery, 24/7 online management.', description: 'Refrigerated, 35 dedicated channels, powerful cooling system, soft delivery, 24/7 online management.',
highlights: ['Refrigerated', '35 dedicated channels', 'Soft delivery', '24/7 online management'], highlights: ['Refrigerated', '35 dedicated channels', 'Soft delivery', '24/7 online management'],
image: '/images/machine-35-base.webp', image: '/images/machine-35-base.webp',
@@ -60,8 +60,8 @@ const machinesEn = [
id: 'rm-48', id: 'rm-48',
name: 'Robot Market RM-48', name: 'Robot Market RM-48',
shortLabel: '48 Channels', shortLabel: '48 Channels',
priceLabel: '4,480,000,000 Rials', priceLabel: '4,980,000,000 Rials',
priceNumeric: 4480000000, priceNumeric: 4980000000,
description: 'Refrigerated, 48 dedicated channels, advanced online management and delivery capabilities.', description: 'Refrigerated, 48 dedicated channels, advanced online management and delivery capabilities.',
highlights: ['Refrigerated', '48 dedicated channels', 'Advanced online management', 'Smart delivery'], highlights: ['Refrigerated', '48 dedicated channels', 'Advanced online management', 'Smart delivery'],
image: '/images/machine-48-base.webp', image: '/images/machine-48-base.webp',
@@ -72,8 +72,8 @@ const machinesEn = [
id: 'rm-60', id: 'rm-60',
name: 'Robot Market RM-60', name: 'Robot Market RM-60',
shortLabel: '60 Channels', shortLabel: '60 Channels',
priceLabel: '4,980,000,000 Rials', priceLabel: '5,480,000,000 Rials',
priceNumeric: 4980000000, priceNumeric: 5480000000,
description: 'Refrigerated, 60 dedicated channels for high-traffic businesses.', description: 'Refrigerated, 60 dedicated channels for high-traffic businesses.',
highlights: ['Refrigerated', '60 dedicated channels', 'Ideal for high-traffic venues', 'High capacity'], highlights: ['Refrigerated', '60 dedicated channels', 'Ideal for high-traffic venues', 'High capacity'],
image: '/images/machine-60-base.webp', image: '/images/machine-60-base.webp',