Skip to content

Commit f862b9b

Browse files
committed
Merge upstream/dev into feature/enhanced-workflow-validation
2 parents ad0f393 + ba1a79e commit f862b9b

65 files changed

Lines changed: 2180 additions & 1236 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/PR-review.yaml

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
name: AI Code Reviewer
2+
3+
on:
4+
issue_comment:
5+
types: [created]
6+
7+
permissions:
8+
contents: read
9+
pull-requests: write
10+
issues: write
11+
12+
jobs:
13+
gemini-code-review:
14+
runs-on: ubuntu-latest
15+
if: |
16+
github.event.issue.pull_request &&
17+
contains(github.event.comment.body, '/gemini-review')
18+
steps:
19+
- name: PR Info
20+
env:
21+
#Assign untrusted inputs to environment variables first
22+
COMMENT_BODY: ${{ github.event.comment.body }}
23+
ISSUE_NUM: ${{ github.event.issue.number }}
24+
REPO: ${{ github.repository }}
25+
#Use shell variables ("$VAR") instead of template tags
26+
run: |
27+
echo "Comment: $COMMENT_BODY"
28+
echo "Issue Number: $ISSUE_NUM"
29+
echo "Repository: $REPO"
30+
31+
- name: Checkout Repo
32+
uses: actions/checkout@v3
33+
with:
34+
fetch-depth: 0
35+
ref: refs/pull/${{ github.event.issue.number }}/head
36+
37+
- name: Get PR Details
38+
id: pr
39+
env:
40+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
41+
REPO: ${{ github.repository }}
42+
ISSUE_NUM: ${{ github.event.issue.number }}
43+
#Use env vars for the API call to prevent injection
44+
#Use quotes around variables to prevent word splitting
45+
run: |
46+
PR_JSON=$(gh api "repos/$REPO/pulls/$ISSUE_NUM")
47+
echo "head_sha=$(echo "$PR_JSON" | jq -r .head.sha)" >> $GITHUB_OUTPUT
48+
echo "base_sha=$(echo "$PR_JSON" | jq -r .base.sha)" >> $GITHUB_OUTPUT
49+
50+
- uses: truongnh1992/gemini-ai-code-reviewer@main
51+
with:
52+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
53+
GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }}
54+
GEMINI_MODEL: gemini-2.5-flash
55+
EXCLUDE: "*.md,*.txt,package-lock.json"

.github/workflows/ci.yml

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches: [main, dev]
6+
pull_request:
7+
branches: [main, dev]
8+
9+
jobs:
10+
lint-and-test:
11+
runs-on: ubuntu-latest
12+
13+
steps:
14+
- name: Checkout repository
15+
uses: actions/checkout@v4
16+
17+
- name: Set up Python
18+
uses: actions/setup-python@v5
19+
with:
20+
python-version: '3.11'
21+
cache: 'pip'
22+
23+
- name: Install dependencies
24+
run: |
25+
python -m pip install --upgrade pip
26+
pip install -r requirements-ci.txt
27+
# Uses minimal CI requirements (no tensorflow/heavy packages)
28+
29+
- name: Run linter (ruff)
30+
run: |
31+
ruff check . --select=E9,F63,F7,F82 --output-format=github \
32+
--exclude="Dockerfile.*" \
33+
--exclude="linktest/" \
34+
--exclude="measurements/" \
35+
--exclude="0mq/" \
36+
--exclude="ratc/"
37+
# E9: Runtime errors (syntax errors, etc.)
38+
# F63: Invalid print syntax
39+
# F7: Syntax errors in type comments
40+
# F82: Undefined names in __all__
41+
# Excludes: Dockerfiles (not Python), linktest (symlinks),
42+
# measurements/0mq/ratc (config-dependent experimental scripts)
43+
44+
- name: Run tests (pytest)
45+
run: |
46+
set +e
47+
pytest --tb=short -q \
48+
--ignore=measurements/ \
49+
--ignore=0mq/ \
50+
--ignore=ratc/ \
51+
--ignore=linktest/
52+
status=$?
53+
set -e
54+
# Allow success if no tests are collected (pytest exit code 5)
55+
if [ "$status" -ne 0 ] && [ "$status" -ne 5 ]; then
56+
exit "$status"
57+
fi
58+
# Fails on real test failures, passes on no tests collected
59+
60+
docker-build:
61+
runs-on: ubuntu-latest
62+
# Only run when Dockerfile.py or related files change
63+
if: |
64+
github.event_name == 'push' ||
65+
(github.event_name == 'pull_request' &&
66+
contains(github.event.pull_request.changed_files, 'Dockerfile'))
67+
68+
steps:
69+
- name: Checkout repository
70+
uses: actions/checkout@v4
71+
72+
- name: Check if Dockerfile.py changed
73+
uses: dorny/paths-filter@v3
74+
id: filter
75+
with:
76+
filters: |
77+
dockerfile:
78+
- 'Dockerfile.py'
79+
- 'requirements.txt'
80+
81+
- name: Validate Dockerfile build
82+
if: steps.filter.outputs.dockerfile == 'true'
83+
run: |
84+
docker build -f Dockerfile.py -t concore-py-test .
85+
# Validates that Dockerfile.py can be built successfully
86+
# Does not push the image

.github/workflows/greetings.yml

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
name: Greetings
2+
3+
on:
4+
pull_request_target:
5+
types: [opened]
6+
issues:
7+
types: [opened]
8+
9+
jobs:
10+
greeting:
11+
runs-on: ubuntu-latest
12+
permissions:
13+
issues: write
14+
pull-requests: write
15+
steps:
16+
- uses: actions/first-interaction@v3
17+
with:
18+
repo_token: ${{ secrets.GITHUB_TOKEN }}
19+
20+
issue_message: |
21+
👋 Welcome to the CONTROL-CORE Project, @${{ github.actor }}! Thank you for opening your first issue in concore.
22+
We appreciate your contribution to the organization and will review it as soon as possible.
23+
24+
Before we get started, please check out these resources:
25+
- 📚 [Project Documentation](https://control-core.readthedocs.io/)
26+
- 📘 [Contribution Guidelines](https://github.com/ControlCore-Project/concore/blob/main/CONTRIBUTING.md)
27+
- 📜 [Code of Conduct](https://github.com/ControlCore-Project/concore/blob/main/CODE_OF_CONDUCT.md)
28+
29+
pr_message: |
30+
🎉 Welcome aboard, @${{ github.actor }}! Thank you for your first pull request in concore.
31+
32+
Please ensure that you are contributing to the **dev** branch.
33+
34+
Your contribution means a lot to us. We'll review it shortly.
35+
36+
Please ensure you have reviewed our:
37+
- 📚 [Project Documentation](https://control-core.readthedocs.io/)
38+
- 📘 [Contribution Guidelines](https://github.com/ControlCore-Project/concore/blob/main/CONTRIBUTING.md)
39+
- 📜 [Code of Conduct](https://github.com/ControlCore-Project/concore/blob/main/CODE_OF_CONDUCT.md)
40+
41+
If you have any questions, feel free to ask. Happy coding!

0mq/comm_node.py

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,21 @@
11
import concore
2-
import concore2
32

43
concore.delay = 0.07
5-
concore2.delay = 0.07
6-
concore2.inpath = concore.inpath
7-
concore2.outpath = concore.outpath
8-
concore2.simtime = 0
4+
concore.simtime = 0
95
concore.default_maxtime(100)
106
init_simtime_u = "[0.0, 0.0, 0.0]"
117
init_simtime_ym = "[0.0, 0.0, 0.0]"
128

139
u = concore.initval(init_simtime_u)
14-
ym = concore2.initval(init_simtime_ym)
15-
while(concore2.simtime<concore.maxtime):
10+
ym = concore.initval(init_simtime_ym)
11+
while(concore.simtime<concore.maxtime):
1612
while concore.unchanged():
1713
u = concore.read(concore.iport['U'],"u",init_simtime_u)
1814
concore.write(concore.oport['U1'],"u",u)
1915
print(u)
20-
old2 = concore2.simtime
21-
while concore2.unchanged() or concore2.simtime <= old2:
22-
ym = concore2.read(concore.iport['Y1'],"ym",init_simtime_ym)
23-
concore2.write(concore.oport['Y'],"ym",ym)
24-
print("funbody u="+str(u)+" ym="+str(ym)+" time="+str(concore2.simtime))
16+
old2 = float(concore.simtime)
17+
while concore.unchanged() or concore.simtime <= old2:
18+
ym = concore.read(concore.iport['Y1'],"ym",init_simtime_ym)
19+
concore.write(concore.oport['Y'],"ym",ym)
20+
print("funbody u="+str(u)+" ym="+str(ym)+" time="+str(concore.simtime))
2521
print("retry="+str(concore.retrycount))

0mq/funbody.py

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import concore
2-
import concore2
32
import time
43
from osparc_control import CommandManifest
54
from osparc_control import CommandParameter
@@ -27,17 +26,14 @@
2726
paired_transmitter.start_background_sync()
2827

2928
concore.delay = 0.07
30-
concore2.delay = 0.07
31-
concore2.inpath = concore.inpath
32-
concore2.outpath = concore.outpath
33-
concore2.simtime = 0
29+
concore.simtime = 0
3430
concore.default_maxtime(100)
3531
init_simtime_u = "[0.0, 0.0, 0.0]"
3632
init_simtime_ym = "[0.0, 0.0, 0.0]"
3733

3834
u = concore.initval(init_simtime_u)
39-
ym = concore2.initval(init_simtime_ym)
40-
while(concore2.simtime<concore.maxtime):
35+
ym = concore.initval(init_simtime_ym)
36+
while(concore.simtime<concore.maxtime):
4137
#while concore.unchanged():
4238
# u = concore.read(concore.iport['U1'],"u",init_simtime_u)
4339
command_list = paired_transmitter.get_incoming_requests()
@@ -53,17 +49,17 @@
5349
u = u[1:]
5450
concore.write(concore.oport['U2'],"u",u)
5551
print(u)
56-
old2 = concore2.simtime
57-
while concore2.unchanged() or concore2.simtime <= old2:
58-
ym = concore2.read(concore.iport['Y2'],"ym",init_simtime_ym)
59-
ym = [concore2.simtime]+ym
52+
old2 = float(concore.simtime)
53+
while concore.unchanged() or concore.simtime <= old2:
54+
ym = concore.read(concore.iport['Y2'],"ym",init_simtime_ym)
55+
ym = [concore.simtime]+ym
6056
print(f"Replying to {command.action} with {ym}")
6157
paired_transmitter.reply_to_command(
6258
request_id=command.request_id, payload=ym)
6359
else:
6460
print("undefined action"+str(command.action))
6561
quit()
66-
#concore2.write(concore.oport['Y1'],"ym",ym)
67-
print("funbody u="+str(u)+" ym="+str(ym)+" time="+str(concore2.simtime))
62+
#concore.write(concore.oport['Y1'],"ym",ym)
63+
print("funbody u="+str(u)+" ym="+str(ym)+" time="+str(concore.simtime))
6864
paired_transmitter.stop_background_sync()
6965
print("retry="+str(concore.retrycount))

0mq/funbody2.py

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import concore
2-
import concore2
32
import time
43
from osparc_control import CommandManifest
54
from osparc_control import CommandParameter
@@ -27,17 +26,14 @@
2726
paired_transmitter.start_background_sync()
2827

2928
concore.delay = 0.07
30-
concore2.delay = 0.07
31-
concore2.inpath = concore.inpath
32-
concore2.outpath = concore.outpath
33-
concore2.simtime = 0
29+
concore.simtime = 0
3430
concore.default_maxtime(100)
3531
init_simtime_u = "[0.0, 0.0, 0.0]"
3632
init_simtime_ym = "[0.0, 0.0, 0.0]"
3733

3834
u = concore.initval(init_simtime_u)
39-
ym = concore2.initval(init_simtime_ym)
40-
while(concore2.simtime<concore.maxtime):
35+
ym = concore.initval(init_simtime_ym)
36+
while(concore.simtime<concore.maxtime):
4137
#while concore.unchanged():
4238
# u = concore.read(concore.iport['U1'],"u",init_simtime_u)
4339
command_list = paired_transmitter.get_incoming_requests()
@@ -53,17 +49,17 @@
5349
u = u[1:]
5450
concore.write(concore.oport['U2'],"u",u)
5551
print(u)
56-
old2 = concore2.simtime
57-
while concore2.unchanged() or concore2.simtime <= old2:
58-
ym = concore2.read(concore.iport['Y2'],"ym",init_simtime_ym)
59-
ym = [concore2.simtime]+ym
52+
old2 = float(concore.simtime)
53+
while concore.simtime <= old2:
54+
ym = concore.read(concore.iport['Y2'],"ym",init_simtime_ym)
55+
ym = [concore.simtime]+ym
6056
print(f"Replying to {command.action} with {ym}")
6157
paired_transmitter.reply_to_command(
6258
request_id=command.request_id, payload=ym)
6359
else:
6460
print("undefined action"+str(command.action))
6561
quit()
66-
#concore2.write(concore.oport['Y1'],"ym",ym)
67-
print("funbody u="+str(u)+" ym="+str(ym)+" time="+str(concore2.simtime))
62+
#concore.write(concore.oport['Y1'],"ym",ym)
63+
print("funbody u="+str(u)+" ym="+str(ym)+" time="+str(concore.simtime))
6864
paired_transmitter.stop_background_sync()
6965
print("retry="+str(concore.retrycount))

0mq/funbody_distributed.py

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
# funbody2_zmq.py
22
import time
33
import concore
4-
import concore2
54

65
print("funbody using ZMQ via concore")
76

@@ -16,21 +15,18 @@
1615

1716
# Standard concore initializations
1817
concore.delay = 0.07
19-
concore2.delay = 0.07
20-
concore2.inpath = concore.inpath
21-
concore2.outpath = concore.outpath
22-
concore2.simtime = 0
18+
concore.simtime = 0
2319
concore.default_maxtime(100)
2420
init_simtime_u_str = "[0.0, 0.0, 0.0]"
2521
init_simtime_ym_str = "[0.0, 0.0, 0.0]"
2622

2723
u_data_values = concore.initval(init_simtime_u_str)
28-
ym_data_values = concore2.initval(init_simtime_ym_str)
24+
ym_data_values = concore.initval(init_simtime_ym_str)
2925

3026
print(f"Initial u_data_values: {u_data_values}, ym_data_values: {ym_data_values}")
3127
print(f"Max time: {concore.maxtime}")
3228

33-
while concore2.simtime < concore.maxtime:
29+
while concore.simtime < concore.maxtime:
3430
received_u_data = concore.read(PORT_NAME_F2_OUT, "u_signal", init_simtime_u_str)
3531

3632
if not (isinstance(received_u_data, list) and len(received_u_data) > 0):
@@ -50,17 +46,17 @@
5046
if 'U2' in concore.oport:
5147
concore.write(concore.oport['U2'], "u", u_data_values)
5248

53-
old_concore2_simtime = concore2.simtime
54-
while concore2.unchanged() or concore2.simtime <= old_concore2_simtime:
49+
old_concore_simtime = float(concore.simtime)
50+
while concore.unchanged() or concore.simtime <= old_concore_simtime:
5551
# Assuming concore.iport['Y2'] is a file port (e.g., from pmpymax.py)
56-
ym_data_values = concore2.read(concore.iport['Y2'], "ym", init_simtime_ym_str)
57-
# time.sleep(concore2.delay) # Optional delay
52+
ym_data_values = concore.read(concore.iport['Y2'], "ym", init_simtime_ym_str)
53+
# time.sleep(concore.delay) # Optional delay
5854

59-
ym_full_to_send = [concore2.simtime] + ym_data_values
55+
ym_full_to_send = [concore.simtime] + ym_data_values
6056

6157
concore.write(PORT_NAME_F2_OUT, "ym_signal", ym_full_to_send)
6258

63-
print(f"funbody u={u_data_values} ym={ym_data_values} time={concore2.simtime}")
59+
print(f"funbody u={u_data_values} ym={ym_data_values} time={concore.simtime}")
6460

6561
print("funbody retry=" + str(concore.retrycount))
6662

0 commit comments

Comments
 (0)