Skip to content

Commit f277914

Browse files
committed
initial commit
1 parent 844a654 commit f277914

File tree

4 files changed

+364
-2
lines changed

4 files changed

+364
-2
lines changed

.github/workflows/release.yml

Lines changed: 253 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,253 @@
1+
name: Build and Release Python CWMS
2+
3+
on:
4+
push:
5+
tags:
6+
- 'v*'
7+
workflow_dispatch:
8+
inputs:
9+
version:
10+
description: 'Version number'
11+
required: true
12+
default: '0.8'
13+
14+
env:
15+
# WinPython Configuration - Update these variables as needed
16+
WINPYTHON_VERSION: "16.6.20250620final"
17+
WINPYTHON_FILENAME: "Winpython64-3.12.10.1dot.zip"
18+
WINPYTHON_DOWNLOAD_URL: "https://github.com/winpython/winpython/releases/download/16.6.20250620final/Winpython64-3.12.10.1dot.zip"
19+
20+
# Alternative: You can also construct the URL from parts
21+
# WINPYTHON_BASE_URL: "https://github.com/winpython/winpython/releases/download"
22+
# Full URL would be: ${{ env.WINPYTHON_BASE_URL }}/${{ env.WINPYTHON_VERSION }}/${{ env.WINPYTHON_FILENAME }}
23+
24+
jobs:
25+
build:
26+
runs-on: windows-latest
27+
28+
steps:
29+
- name: Checkout repository
30+
uses: actions/checkout@v4
31+
32+
- name: Download WinPython
33+
run: |
34+
$url = "${{ env.WINPYTHON_DOWNLOAD_URL }}"
35+
$filename = "${{ env.WINPYTHON_FILENAME }}"
36+
37+
Write-Host "Downloading WinPython from: $url"
38+
Write-Host "Saving as: $filename"
39+
40+
Invoke-WebRequest -Uri $url -OutFile $filename
41+
42+
# Verify the file was downloaded
43+
if (Test-Path $filename) {
44+
$size = (Get-Item $filename).Length / 1MB
45+
Write-Host "✓ Downloaded successfully - Size: $([math]::Round($size, 2)) MB"
46+
} else {
47+
Write-Error "❌ Failed to download $filename"
48+
exit 1
49+
}
50+
51+
- name: Extract WinPython
52+
run: |
53+
$filename = "${{ env.WINPYTHON_FILENAME }}"
54+
Write-Host "Extracting $filename..."
55+
56+
Expand-Archive -Path $filename -DestinationPath "winpython_extracted"
57+
58+
# List what was extracted
59+
$extractedDirs = Get-ChildItem -Path "winpython_extracted" -Directory
60+
Write-Host "Extracted directories:"
61+
$extractedDirs | ForEach-Object { Write-Host " - $($_.Name)" }
62+
63+
- name: Install packages from requirements
64+
run: |
65+
$winpythonDir = Get-ChildItem -Path "winpython_extracted" -Directory | Select-Object -First 1
66+
Write-Host "Using WinPython directory: $($winpythonDir.Name)"
67+
68+
$pipPath = Join-Path $winpythonDir.FullName "python-*\Scripts\pip.exe"
69+
$pipExe = Get-ChildItem -Path $pipPath | Select-Object -First 1
70+
71+
if (Test-Path "requirements_binary_only.txt") {
72+
Write-Host "Installing packages from requirements_binary_only.txt..."
73+
& $pipExe.FullName install -r requirements_binary_only.txt --only-binary=all
74+
} else {
75+
Write-Host "No requirements_binary_only.txt found, skipping package installation"
76+
}
77+
78+
- name: Run setup script (if exists)
79+
run: |
80+
$winpythonDir = Get-ChildItem -Path "winpython_extracted" -Directory | Select-Object -First 1
81+
$pythonPath = Join-Path $winpythonDir.FullName "python-*\python.exe"
82+
$pythonExe = Get-ChildItem -Path $pythonPath | Select-Object -First 1
83+
84+
if (Test-Path "setup_environment.py") {
85+
Write-Host "Running setup_environment.py..."
86+
& $pythonExe.FullName setup_environment.py
87+
} else {
88+
Write-Host "No setup_environment.py found, skipping custom setup"
89+
}
90+
91+
- name: Create final directory structure
92+
run: |
93+
$winpythonDir = Get-ChildItem -Path "winpython_extracted" -Directory | Select-Object -First 1
94+
$version = if ($env:GITHUB_EVENT_NAME -eq "workflow_dispatch") {
95+
"${{ github.event.inputs.version }}"
96+
} else {
97+
"${{ github.ref_name }}" -replace "^v", ""
98+
}
99+
$finalDir = "pythonCWMS$version"
100+
101+
Write-Host "Creating final directory: $finalDir"
102+
Write-Host "Source WinPython directory: $($winpythonDir.Name)"
103+
104+
New-Item -ItemType Directory -Path $finalDir -Force
105+
Copy-Item -Path "$($winpythonDir.FullName)\*" -Destination $finalDir -Recurse -Force
106+
107+
# Copy any additional files from repo (excluding git files)
108+
Write-Host "Copying additional repository files..."
109+
Get-ChildItem -Path "." -Exclude ".git*", "winpython_extracted", "*.zip", $finalDir |
110+
ForEach-Object {
111+
Write-Host " Copying: $($_.Name)"
112+
Copy-Item -Path $_.FullName -Destination $finalDir -Recurse -Force
113+
}
114+
115+
echo "FINAL_DIR=$finalDir" >> $env:GITHUB_ENV
116+
echo "VERSION=$version" >> $env:GITHUB_ENV
117+
118+
# Store the WinPython subdirectory name for the config
119+
$winpythonSubDir = $winpythonDir.Name
120+
echo "WINPYTHON_SUBDIR=$winpythonSubDir" >> $env:GITHUB_ENV
121+
Write-Host "WinPython subdirectory: $winpythonSubDir"
122+
123+
- name: Install 7-Zip
124+
run: |
125+
Write-Host "Installing 7-Zip..."
126+
choco install 7zip -y
127+
128+
- name: Create 7z archive
129+
run: |
130+
$archiveName = "pythonCWMS${{ env.VERSION }}.7z"
131+
Write-Host "Creating archive: $archiveName"
132+
Write-Host "Compressing directory: ${{ env.FINAL_DIR }}"
133+
134+
& "C:\Program Files\7-Zip\7z.exe" a -t7z -mx=9 $archiveName "${{ env.FINAL_DIR }}"
135+
136+
if (Test-Path $archiveName) {
137+
$size = (Get-Item $archiveName).Length / 1MB
138+
Write-Host "✓ Archive created successfully - Size: $([math]::Round($size, 2)) MB"
139+
echo "ARCHIVE_SIZE_MB=$([math]::Round($size, 2))" >> $env:GITHUB_ENV
140+
} else {
141+
Write-Error "❌ Failed to create archive"
142+
exit 1
143+
}
144+
145+
echo "ARCHIVE_NAME=$archiveName" >> $env:GITHUB_ENV
146+
147+
- name: Calculate SHA256 hash
148+
run: |
149+
Write-Host "Calculating SHA256 hash for ${{ env.ARCHIVE_NAME }}..."
150+
$hash = Get-FileHash -Path "${{ env.ARCHIVE_NAME }}" -Algorithm SHA256
151+
$hashString = $hash.Hash
152+
echo "ARCHIVE_HASH=$hashString" >> $env:GITHUB_ENV
153+
Write-Host "✓ SHA256: $hashString"
154+
155+
- name: Generate config JSON
156+
run: |
157+
$repoOwner = "${{ github.repository_owner }}"
158+
$repoName = "${{ github.event.repository.name }}"
159+
$version = "${{ env.VERSION }}"
160+
$archiveName = "${{ env.ARCHIVE_NAME }}"
161+
$hash = "${{ env.ARCHIVE_HASH }}"
162+
$winpythonSubDir = "${{ env.WINPYTHON_SUBDIR }}"
163+
$winpythonVersion = "${{ env.WINPYTHON_VERSION }}"
164+
$winpythonFilename = "${{ env.WINPYTHON_FILENAME }}"
165+
166+
Write-Host "Generating configuration file..."
167+
168+
# Construct the download URL
169+
$downloadUrl = "https://github.com/$repoOwner/$repoName/releases/download/v$version/$archiveName"
170+
171+
# Find the python subdirectory (should be something like python-3.12.10.amd64)
172+
$pythonSubDir = Get-ChildItem -Path "${{ env.FINAL_DIR }}" -Directory -Name "python-*" | Select-Object -First 1
173+
$pythonExeSubDir = "$winpythonSubDir\$pythonSubDir"
174+
175+
Write-Host "Python executable subdirectory: $pythonExeSubDir"
176+
177+
# Create the config object
178+
$config = @{
179+
python_download_url = $downloadUrl
180+
python_expected_hash_sha256 = $hash
181+
default_install_directory = "C:\hec\python"
182+
default_env_var_name = "PYTHON_CWMS_HOME"
183+
python_exe_sub_directory = $pythonExeSubDir
184+
version = $version
185+
archive_filename = $archiveName
186+
archive_size_mb = [math]::Round((Get-Item "${{ env.ARCHIVE_NAME }}").Length / 1MB, 2)
187+
created_date = (Get-Date -Format "yyyy-MM-ddTHH:mm:ssZ")
188+
source_winpython_version = $winpythonVersion
189+
source_winpython_filename = $winpythonFilename
190+
}
191+
192+
# Convert to JSON and save
193+
$configJson = $config | ConvertTo-Json -Depth 10
194+
$configJson | Out-File -FilePath "pythonCWMS_config.json" -Encoding UTF8
195+
196+
Write-Host "✓ Generated config file:"
197+
Get-Content "pythonCWMS_config.json"
198+
199+
- name: Validate config file
200+
run: |
201+
Write-Host "Validating configuration file..."
202+
# Test that the JSON is valid
203+
try {
204+
$config = Get-Content "pythonCWMS_config.json" | ConvertFrom-Json
205+
Write-Host "✓ Config JSON is valid"
206+
Write-Host "✓ Download URL: $($config.python_download_url)"
207+
Write-Host "✓ Hash: $($config.python_expected_hash_sha256)"
208+
Write-Host "✓ Python exe path: $($config.python_exe_sub_directory)"
209+
Write-Host "✓ Source WinPython: $($config.source_winpython_filename)"
210+
}
211+
catch {
212+
Write-Error "❌ Config JSON is invalid: $_"
213+
exit 1
214+
}
215+
216+
- name: Create Release
217+
uses: softprops/action-gh-release@v1
218+
with:
219+
tag_name: ${{ github.ref_name || format('v{0}', github.event.inputs.version) }}
220+
name: Python CWMS ${{ env.VERSION }}
221+
draft: false
222+
prerelease: false
223+
files: |
224+
${{ env.ARCHIVE_NAME }}
225+
pythonCWMS_config.json
226+
body: |
227+
## Python CWMS ${{ env.VERSION }}
228+
229+
Portable Python environment with CWMS libraries and dependencies.
230+
231+
### Downloads:
232+
- **`${{ env.ARCHIVE_NAME }}`** - Main Python environment archive (${{ env.ARCHIVE_SIZE_MB }} MB)
233+
- **`pythonCWMS_config.json`** - Configuration file for automated installers
234+
235+
### Archive Details:
236+
- **Size:** ${{ env.ARCHIVE_SIZE_MB }} MB
237+
- **SHA256:** `${{ env.ARCHIVE_HASH }}`
238+
- **Source:** ${{ env.WINPYTHON_FILENAME }}
239+
240+
### Contents:
241+
- WinPython 3.12.10.1 (from ${{ env.WINPYTHON_VERSION }})
242+
- Libraries from requirements_binary_only.txt
243+
- Custom setup and configuration
244+
245+
### Usage:
246+
1. Extract the .7z file to your desired location
247+
2. Run `${{ env.FINAL_DIR }}\WinPython Command Prompt.exe`
248+
3. Your environment is ready to use!
249+
250+
### For Automated Installation:
251+
Use the `pythonCWMS_config.json` file with your installer scripts.
252+
env:
253+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

.gitignore

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# Python
2+
__pycache__/
3+
*.pyc
4+
*.pyo
5+
*.pyd
6+
.Python
7+
env/
8+
venv/
9+
.env
10+
11+
# WinPython downloads and extracts
12+
Winpython64-*.zip
13+
winpython_extracted/
14+
pythonCWMS*/
15+
16+
# Archives
17+
*.7z
18+
*.zip
19+
*.tar.gz
20+
21+
# IDE
22+
.vscode/
23+
.idea/
24+
*.swp
25+
*.swo
26+
27+
# OS
28+
.DS_Store
29+
Thumbs.db

README.md

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,38 @@
1-
# pythonCWMS
2-
A portable, Windows, python distribution with common libraries preinstalled for use with CWMS
1+
# Python CWMS Portable Environment
2+
3+
A portable, Windows, Python environment bundled with CWMS libraries and dependencies.
4+
5+
## What's Included
6+
7+
- **WinPython 3.12.10.1**: Portable Python distribution
8+
- **Pre-installed Libraries**: All dependencies from `requirements_binary_only.txt`
9+
- **Custom Configuration**: CWMS-specific setup and utilities
10+
11+
## Quick Start
12+
13+
### Download
14+
Go to [Releases](../../releases) and download the latest `pythonCWMS*.7z` file.
15+
16+
### Installation
17+
1. Extract the `.7z` file to your desired location
18+
2. No installation required - it's completely portable!
19+
20+
### Usage
21+
- Run `WinPython Command Prompt.exe` for command line access
22+
- Run `WinPython Interpreter.exe` for Python IDLE
23+
- Or use `start_cwms.bat` for the custom CWMS environment
24+
25+
## Development
26+
27+
### Building Locally
28+
1. Clone this repository
29+
2. Create/modify `requirements_binary_only.txt` with your dependencies
30+
3. Push a tag to trigger the build: `git tag v0.8 && git push origin v0.8`
31+
32+
### Manual Build
33+
You can also trigger a build manually from the Actions tab.
34+
35+
## Requirements File
36+
37+
The `requirements_binary_only.txt` file contains all Python packages to be installed. Only binary wheels are used to ensure compatibility and faster installation.
38+

requirements_binary_only.txt

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
--only-binary=:all:
2+
--prefer-binary
3+
beautifulsoup4
4+
bokeh
5+
certifi
6+
charset-normalizer
7+
contourpy
8+
cycler
9+
fonttools
10+
idna
11+
importlib_resources
12+
Jinja2
13+
kiwisolver
14+
MarkupSafe
15+
matplotlib
16+
numpy
17+
packaging
18+
pandas
19+
pillow
20+
pyparsing
21+
python-dateutil
22+
python-dotenv
23+
pytz
24+
PyYAML
25+
requests
26+
requests-toolbelt
27+
seaborn
28+
six
29+
soupsieve
30+
tornado
31+
urllib3
32+
xarray
33+
xyzservices
34+
zipp
35+
jupyterlab
36+
plotly
37+
pip-system-certs
38+
requests
39+
hec-python-library
40+
cwms-python
41+
dataretrieval
42+
hyswap
43+
hecdss
44+
metar

0 commit comments

Comments
 (0)