Skip to content

Commit 2aba2f4

Browse files
committed
feat(ci): Port all workflow updates from development branch
1 parent 9828f83 commit 2aba2f4

7 files changed

Lines changed: 427 additions & 158 deletions

.github/workflows/conda_build.yml

Lines changed: 23 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ jobs:
1414
# First job: Wait for PyPI to have the package
1515
wait-for-pypi:
1616
runs-on: ubuntu-latest
17+
# Add a timeout to this specific job
18+
timeout-minutes: 25
1719
outputs:
1820
version: ${{ steps.get-version.outputs.version }}
1921
sha256: ${{ steps.get-sha.outputs.sha256 }}
@@ -23,6 +25,7 @@ jobs:
2325
run: |
2426
if [ "${{ github.event_name }}" == "release" ]; then
2527
VERSION="${{ github.event.release.tag_name }}"
28+
# Remove 'v' prefix if it exists
2629
VERSION="${VERSION#v}"
2730
else
2831
VERSION="${{ github.event.inputs.version }}"
@@ -34,10 +37,11 @@ jobs:
3437
id: wait-pypi
3538
run: |
3639
VERSION="${{ steps.get-version.outputs.version }}"
37-
MAX_ATTEMPTS=20
40+
# Increased attempts and sleep time for a total wait of up to 20 minutes
41+
MAX_ATTEMPTS=40
3842
SLEEP_TIME=30
3943
40-
echo "Checking for omnipkg version $VERSION on PyPI..."
44+
echo "Checking for omnipkg version $VERSION on PyPI (will try for up to 20 minutes)..."
4145
4246
for i in $(seq 1 $MAX_ATTEMPTS); do
4347
echo "Attempt $i/$MAX_ATTEMPTS..."
@@ -49,18 +53,20 @@ jobs:
4953
if [ "$HTTP_CODE" = "200" ]; then
5054
echo "✓ Package version found on PyPI API!"
5155
52-
# Also verify the tar.gz file is downloadable
56+
# Also verify the tar.gz file is downloadable from the CDN
5357
DOWNLOAD_URL="https://files.pythonhosted.org/packages/source/o/omnipkg/omnipkg-$VERSION.tar.gz"
5458
DOWNLOAD_CODE=$(curl -s -o /dev/null -w "%{http_code}" -I "$DOWNLOAD_URL")
5559
5660
if [ "$DOWNLOAD_CODE" = "200" ] || [ "$DOWNLOAD_CODE" = "302" ]; then
57-
echo "✓ Package file is downloadable!"
61+
echo "✓ Package file is downloadable! Proceeding."
62+
# Give it a few extra seconds just in case
63+
sleep 5
5864
break
5965
else
60-
echo "⚠ Package found in API but file not yet available (HTTP $DOWNLOAD_CODE)"
66+
echo "⚠ Package found in API but file not yet available on CDN (HTTP $DOWNLOAD_CODE)"
6167
fi
6268
else
63-
echo "Package not yet available (HTTP $HTTP_CODE)"
69+
echo "Package not yet available on API (HTTP $HTTP_CODE)"
6470
fi
6571
6672
if [ $i -eq $MAX_ATTEMPTS ]; then
@@ -69,7 +75,7 @@ jobs:
6975
exit 1
7076
fi
7177
72-
echo "Waiting ${SLEEP_TIME}s..."
78+
echo "Waiting ${SLEEP_TIME}s before next attempt..."
7379
sleep $SLEEP_TIME
7480
done
7581
@@ -79,32 +85,22 @@ jobs:
7985
VERSION="${{ steps.get-version.outputs.version }}"
8086
8187
echo "Downloading package from PyPI..."
82-
# Try the files.pythonhosted.org URL first (CDN)
8388
if ! curl -L -f -o "omnipkg-$VERSION.tar.gz" \
8489
"https://files.pythonhosted.org/packages/source/o/omnipkg/omnipkg-$VERSION.tar.gz"; then
85-
# Fallback to pypi.org URL
86-
echo "CDN failed, trying pypi.org..."
90+
echo "CDN failed, trying pypi.org fallback..."
8791
curl -L -f -o "omnipkg-$VERSION.tar.gz" \
8892
"https://pypi.org/packages/source/o/omnipkg/omnipkg-$VERSION.tar.gz"
8993
fi
9094
91-
# Verify file size
9295
FILE_SIZE=$(wc -c < "omnipkg-$VERSION.tar.gz")
9396
echo "File size: $FILE_SIZE bytes"
94-
9597
if [ "$FILE_SIZE" -lt 10000 ]; then
96-
echo "Error: File too small ($FILE_SIZE bytes)"
98+
echo "Error: Downloaded file is too small ($FILE_SIZE bytes), which is suspicious."
9799
exit 1
98100
fi
99101
100102
echo "Calculating SHA256..."
101-
if command -v sha256sum &> /dev/null; then
102-
SHA256=$(sha256sum "omnipkg-$VERSION.tar.gz" | cut -d' ' -f1)
103-
elif command -v shasum &> /dev/null; then
104-
SHA256=$(shasum -a 256 "omnipkg-$VERSION.tar.gz" | cut -d' ' -f1)
105-
else
106-
SHA256=$(python3 -c "import hashlib; print(hashlib.sha256(open('omnipkg-$VERSION.tar.gz', 'rb').read()).hexdigest())")
107-
fi
103+
SHA256=$(sha256sum "omnipkg-$VERSION.tar.gz" | cut -d' ' -f1)
108104
109105
echo "sha256=$SHA256" >> $GITHUB_OUTPUT
110106
echo "SHA256: $SHA256"
@@ -113,8 +109,11 @@ jobs:
113109
update-feedstock:
114110
needs: wait-for-pypi
115111
runs-on: ubuntu-latest
112+
# Add a timeout to this specific job
113+
timeout-minutes: 20
116114
if: github.event_name == 'release'
117115
steps:
116+
# ... (steps for this job remain the same) ...
118117
- name: Checkout your feedstock fork
119118
uses: actions/checkout@v4
120119
with:
@@ -135,11 +134,9 @@ jobs:
135134
136135
echo "Updating recipe/meta.yaml to version $VERSION with SHA256: $SHA256"
137136
138-
# Update version and sha256 in meta.yaml
139137
sed -i "s/{% set version = \".*\" %}/{% set version = \"$VERSION\" %}/" recipe/meta.yaml
140138
sed -i "s/sha256: .*/sha256: $SHA256/" recipe/meta.yaml
141139
142-
# Show the changes
143140
echo "=== Updated meta.yaml (first 30 lines) ==="
144141
head -30 recipe/meta.yaml
145142
@@ -178,16 +175,20 @@ jobs:
178175
base: main
179176
push-to-fork: 1minds3t/omnipkg-feedstock
180177

178+
181179
# Third job: Build conda packages for your own channel
182180
build-and-upload:
183181
needs: wait-for-pypi
184182
runs-on: ${{ matrix.os }}
183+
# Add a timeout to this job, which applies to each matrix instance
184+
timeout-minutes: 45
185185
strategy:
186186
matrix:
187187
os: [ubuntu-latest, windows-latest, macos-latest]
188188
python-version: ['3.10', '3.11', '3.12']
189189

190190
steps:
191+
# ... (steps for this job remain the same) ...
191192
- name: Checkout code
192193
uses: actions/checkout@v4
193194

@@ -212,7 +213,6 @@ jobs:
212213
213214
echo "Using version $VERSION with SHA256: $SHA256"
214215
215-
# Update the version and sha256 in meta.yaml (cross-platform sed)
216216
if [[ "$RUNNER_OS" == "macOS" ]]; then
217217
sed -i '' "s/{% set version = \".*\" %}/{% set version = \"$VERSION\" %}/" omnipkg/conda-recipe/meta.yaml
218218
sed -i '' "s/sha256: .*/sha256: $SHA256/" omnipkg/conda-recipe/meta.yaml
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
name: CI - Build and Push to GHCR
2+
3+
on:
4+
push:
5+
branches: [ main ]
6+
workflow_dispatch:
7+
8+
env:
9+
REGISTRY: ghcr.io
10+
IMAGE_NAME: ${{ github.repository }}
11+
12+
jobs:
13+
build-and-push-ghcr:
14+
runs-on: ubuntu-latest
15+
permissions:
16+
contents: read
17+
packages: write
18+
19+
steps:
20+
- name: Checkout repository
21+
uses: actions/checkout@v4
22+
23+
- name: Set up Docker Buildx
24+
uses: docker/setup-buildx-action@v3
25+
26+
- name: Log in to GitHub Container Registry
27+
uses: docker/login-action@v3
28+
with:
29+
registry: ${{ env.REGISTRY }}
30+
username: ${{ github.actor }}
31+
password: ${{ secrets.GITHUB_TOKEN }}
32+
33+
- name: Extract metadata (tags, labels)
34+
id: meta
35+
uses: docker/metadata-action@v5
36+
with:
37+
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
38+
# This will tag the main branch commit with 'latest'
39+
tags: |
40+
type=raw,value=latest,enable={{is_default_branch}}
41+
42+
- name: Build and push to GHCR
43+
uses: docker/build-push-action@v5
44+
with:
45+
context: .
46+
platforms: linux/amd64,linux/arm64
47+
push: true
48+
tags: ${{ steps.meta.outputs.tags }}
49+
labels: ${{ steps.meta.outputs.labels }}
50+
cache-from: type=gha
51+
cache-to: type=gha,mode=max
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
name: RELEASE - Publish to Docker Hub
2+
3+
on:
4+
release:
5+
types: [published]
6+
7+
jobs:
8+
build-and-push-dockerhub:
9+
runs-on: ubuntu-latest
10+
permissions:
11+
contents: read
12+
13+
steps:
14+
- name: Checkout repository
15+
uses: actions/checkout@v4
16+
17+
- name: Set up Docker Buildx
18+
uses: docker/setup-buildx-action@v3
19+
20+
- name: Log in to Docker Hub
21+
uses: docker/login-action@v3
22+
with:
23+
username: 1minds3t
24+
password: ${{ secrets.DOCKERHUB_TOKEN }}
25+
26+
- name: Extract metadata for Docker Hub
27+
id: meta
28+
uses: docker/metadata-action@v5
29+
with:
30+
images: 1minds3t/omnipkg
31+
# This creates tags like: 1.5.7, 1.5, and latest
32+
tags: |
33+
type=semver,pattern={{version}}
34+
type=semver,pattern={{major}}.{{minor}}
35+
type=raw,value=latest,enable={{is_default_branch}}
36+
37+
- name: Build and push to Docker Hub
38+
uses: docker/build-push-action@v5
39+
with:
40+
context: .
41+
platforms: linux/amd64,linux/arm64
42+
push: true
43+
tags: ${{ steps.meta.outputs.tags }}
44+
labels: ${{ steps.meta.outputs.labels }}
45+
# You can still leverage the GHA cache from the CI workflow
46+
cache-from: type=gha
47+
cache-to: type=gha,mode=max
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
name: "🧪 Flask Port Finder & Auto-Healing Test"
2+
3+
on:
4+
push:
5+
branches: [ main ]
6+
pull_request:
7+
branches: [ main ]
8+
workflow_dispatch:
9+
10+
jobs:
11+
test:
12+
runs-on: ubuntu-latest
13+
services:
14+
redis:
15+
image: redis:7
16+
options: >-
17+
--health-cmd "redis-cli ping"
18+
--health-interval 10s
19+
--health-timeout 5s
20+
--health-retries 5
21+
ports:
22+
- 6379:6379
23+
24+
steps:
25+
- name: Checkout repository
26+
uses: actions/checkout@v4
27+
28+
- name: Set up Python 3.11
29+
uses: actions/setup-python@v5
30+
with:
31+
python-version: '3.11'
32+
33+
- name: 📦 Install omnipkg (editable) and Redis client
34+
run: |
35+
python -m pip install --upgrade pip
36+
pip install -e . redis
37+
38+
- name: Configure omnipkg for non-interactive use
39+
run: |
40+
python - << 'EOF'
41+
import sys
42+
import site
43+
import json
44+
from pathlib import Path
45+
import os
46+
import sysconfig
47+
48+
try:
49+
site_packages_path = site.getsitepackages()[0]
50+
except (IndexError, AttributeError):
51+
site_packages_path = sysconfig.get_paths()['purelib']
52+
53+
project_root = Path(os.environ['GITHUB_WORKSPACE'])
54+
55+
builder_script = project_root / 'omnipkg' / 'package_meta_builder.py'
56+
if not builder_script.exists():
57+
print(f"Error: {builder_script} does not exist")
58+
sys.exit(1)
59+
60+
config_data = {
61+
'site_packages_path': site_packages_path,
62+
'multiversion_base': str(Path(site_packages_path) / '.omnipkg_versions'),
63+
'python_executable': sys.executable,
64+
'builder_script_path': str(builder_script),
65+
'redis_host': 'localhost',
66+
'redis_port': 6379,
67+
'redis_key_prefix': 'omnipkg:pkg:',
68+
'paths_to_index': [str(Path(sys.executable).parent), '/usr/local/bin', '/usr/bin', '/bin', '/usr/sbin', '/sbin'],
69+
'auto_cleanup': True,
70+
'cleanup_threshold_days': 30
71+
}
72+
73+
config_dir = Path.home() / '.config' / 'omnipkg'
74+
config_dir.mkdir(parents=True, exist_ok=True)
75+
config_path = config_dir / 'config.json'
76+
77+
try:
78+
with open(config_path, 'w') as f:
79+
json.dump(config_data, f, indent=2)
80+
print(f'omnipkg config created at {config_path}:')
81+
print(json.dumps(config_data, indent=2))
82+
except Exception as e:
83+
print(f"Error writing config: {e}")
84+
sys.exit(1)
85+
EOF
86+
87+
- name: 🧪 Run the Demo - Flask Port Finder Test
88+
id: run_demo
89+
run: |
90+
echo "--- Running Omnipkg Demo for Flask Port Finder (Auto-Healing) ---"
91+
mkdir -p /tmp/omnipkg-artifacts
92+
93+
# Run demo #9 and capture output and the correct exit code from the omnipkg command
94+
timeout 900 bash -c 'echo "9" | omnipkg demo 2>&1 | tee /tmp/omnipkg-artifacts/flask_demo_output.txt'
95+
DEMO_EXIT_CODE=${PIPESTATUS[0]}
96+
97+
# Save output to GitHub step summary for easy viewing
98+
echo "## Flask Port Finder Demo Output" >> $GITHUB_STEP_SUMMARY
99+
echo '```' >> $GITHUB_STEP_SUMMARY
100+
cat /tmp/omnipkg-artifacts/flask_demo_output.txt >> $GITHUB_STEP_SUMMARY
101+
echo '```' >> $GITHUB_STEP_SUMMARY
102+
103+
# The overall script should exit with 0 if the final, healed run was successful.
104+
# We add extra checks of the log content for robustness.
105+
if [ $DEMO_EXIT_CODE -eq 0 ]; then
106+
echo "Demo completed with successful exit code ($DEMO_EXIT_CODE). Performing log verification..."
107+
echo "demo_outcome=success" >> $GITHUB_OUTPUT
108+
109+
# Count the number of passed tests in the final summary block
110+
PASSED_COUNT=$(grep -c "✅ TEST .* PASSED" /tmp/omnipkg-artifacts/flask_demo_output.txt || echo "0")
111+
112+
# Check that all 6 tests are marked as passed
113+
if [ "$PASSED_COUNT" -ge 6 ] && grep -q "Demo completed successfully" /tmp/omnipkg-artifacts/flask_demo_output.txt; then
114+
echo "✅ Verification PASSED: Found $PASSED_COUNT passed tests and success message in the logs."
115+
else
116+
echo "❌ Verification FAILED: Expected 6 PASSED tests and a success message, but validation failed."
117+
echo "--- Full Demo Output ---"
118+
cat /tmp/omnipkg-artifacts/flask_demo_output.txt
119+
exit 1
120+
fi
121+
else
122+
echo "❌ Demo failed with a non-zero exit code: $DEMO_EXIT_CODE."
123+
echo "demo_outcome=failure" >> $GITHUB_OUTPUT
124+
echo "--- Full Demo Output ---"
125+
cat /tmp/omnipkg-artifacts/flask_demo_output.txt
126+
exit 1
127+
fi
128+
129+
- name: Archive Demo Output
130+
if: always()
131+
uses: actions/upload-artifact@v4
132+
with:
133+
name: omnipkg-flask-demo-output
134+
path: /tmp/omnipkg-artifacts/
135+
retention-days: 7
136+
compression-level: 6

0 commit comments

Comments
 (0)