initial attempt at a deletion tool

This commit is contained in:
Gregory Gauthier 2026-02-06 14:55:15 +00:00
parent 2d657c0809
commit 12255afeea
2 changed files with 135 additions and 9 deletions

View File

@ -31,7 +31,7 @@ jobs:
nc -h nc -h
echo "=== Debug: Setup complete ===" echo "=== Debug: Setup complete ==="
- name: Find changed source files - name: Find changed and deleted source files
id: files id: files
run: | run: |
echo "=== Debug: Starting file detection ===" echo "=== Debug: Starting file detection ==="
@ -41,13 +41,16 @@ jobs:
if git rev-parse --verify HEAD~1 >/dev/null 2>&1; then if git rev-parse --verify HEAD~1 >/dev/null 2>&1; then
echo "Parent commit exists; running git diff." echo "Parent commit exists; running git diff."
CHANGED_FILES=$(git diff --name-only HEAD~1 2>/dev/null | grep -E '\.(c|bas)$' | head -1) CHANGED_FILES=$(git diff --name-only HEAD~1 2>/dev/null | grep -E '\.(c|bas)$' | head -1)
DELETED_FILES=$(git diff --name-only --diff-filter=D HEAD~1 2>/dev/null | grep -E '\.(c|bas)$')
echo "Changed files from last commit: '${CHANGED_FILES}'" echo "Changed files from last commit: '${CHANGED_FILES}'"
echo "Deleted files from last commit: '${DELETED_FILES}'"
else else
echo "No parent commit; skipping diff." echo "No parent commit; skipping diff."
CHANGED_FILES="" CHANGED_FILES=""
DELETED_FILES=""
fi fi
echo "=== Debug: Git diff check complete ===" echo "=== Debug: Git diff check complete ==="
# Fallback to all .c/.bas files if no changes or no previous commit # Fallback to all .c/.bas files if no changes or no previous commit
if [ -z "$CHANGED_FILES" ]; then if [ -z "$CHANGED_FILES" ]; then
echo "=== Debug: No changes found; running fallback find ===" echo "=== Debug: No changes found; running fallback find ==="
@ -56,22 +59,49 @@ jobs:
echo "Fallback files (newest first): '${CHANGED_FILES}'" echo "Fallback files (newest first): '${CHANGED_FILES}'"
echo "=== Debug: Fallback complete ===" echo "=== Debug: Fallback complete ==="
fi fi
if [ -z "$CHANGED_FILES" ]; then if [ -z "$CHANGED_FILES" ] && [ -z "$DELETED_FILES" ]; then
echo "No C/BAS files found; skipping workflow." echo "No C/BAS files found; skipping workflow."
exit 0 # Graceful skip, no failure exit 0 # Graceful skip, no failure
fi fi
echo "=== Debug: Processing final file ===" echo "=== Debug: Processing final file ==="
echo "Final selected file: '${CHANGED_FILES}'" echo "Final selected file: '${CHANGED_FILES}'"
echo "file=$CHANGED_FILES" >> $GITHUB_OUTPUT echo "file=$CHANGED_FILES" >> $GITHUB_OUTPUT
# Extract member name (handle .c or .bas) # Extract member name (handle .c or .bas)
EXT="${CHANGED_FILES##*.}" if [ -n "$CHANGED_FILES" ]; then
BASE=$(basename "$CHANGED_FILES" ".$EXT") EXT="${CHANGED_FILES##*.}"
echo "member=$BASE" >> $GITHUB_OUTPUT BASE=$(basename "$CHANGED_FILES" ".$EXT")
echo "member=$BASE" >> $GITHUB_OUTPUT
fi
# Process deleted files - convert to space-separated list of members
if [ -n "$DELETED_FILES" ]; then
DELETED_MEMBERS=""
for DFILE in $DELETED_FILES; do
DEXT="${DFILE##*.}"
DBASE=$(basename "$DFILE" ".$DEXT")
DELETED_MEMBERS="$DELETED_MEMBERS $DBASE"
done
echo "deleted_members=$DELETED_MEMBERS" >> $GITHUB_OUTPUT
fi
echo "=== Debug: File detection complete ===" echo "=== Debug: File detection complete ==="
- name: Delete removed members from PDS
if: ${{ steps.files.outputs.deleted_members != '' }}
run: |
echo "=== Debug: Starting deletion of removed members ==="
echo "Deleted members: ${{ steps.files.outputs.deleted_members }}"
for MEMBER in ${{ steps.files.outputs.deleted_members }}; do
echo "Deleting member: $MEMBER"
python3 delete_mvs_member.py "@05054.SRCLIB.C($MEMBER)"
done
echo "=== Debug: Deletion complete ==="
env:
MVS_BATCH_PASSWORD: ${{ vars.MVS_BATCH_PASSWORD }}
MVS_HOST: "oldcomputernerd.com"
- name: Upload to PDS and Submit JCL - name: Upload to PDS and Submit JCL
if: ${{ steps.files.outputs.file != '' }} if: ${{ steps.files.outputs.file != '' }}
run: | run: |

96
delete_mvs_member.py Normal file
View File

@ -0,0 +1,96 @@
#!/usr/bin/env python3
import sys
import subprocess
import tempfile
import os
# Force temp files into a folder inside your project
custom_temp_dir = os.path.join(os.getcwd(), "tmp")
os.makedirs(custom_temp_dir, exist_ok=True)
tempfile.tempdir = custom_temp_dir
MVSHOST = "oldcomputernerd.com"
RDRPORT = 3505
MVS_PASSWORD = os.environ.get("MVS_BATCH_PASSWORD")
def create_delete_jcl(dataset_name, member_name):
"""Create JCL to delete a PDS member using IEHPROGM"""
jcl = f"""
//DELETE JOB (ACCT),'DELETE',
// USER=@05054,PASSWORD={MVS_PASSWORD},
// CLASS=A,MSGCLASS=H,NOTIFY=@05054
//DELMEM EXEC PGM=IEHPROGM
//SYSPRINT DD SYSOUT=*
//DD1 DD DSN={dataset_name},DISP=SHR
//SYSIN DD *
DELETE DSNAME={dataset_name},MEMBER={member_name}
/*
"""
return jcl
def delete_member(dataset_name, member_name, mvshost=MVSHOST):
"""Delete a member from MVS PDS"""
payload = create_delete_jcl(dataset_name, member_name)
# Write JCL to temporary file and submit via netcat
with tempfile.NamedTemporaryFile(mode='w', delete=False, suffix='.jcl') as tmpfile:
tmpfile.write(payload)
tmpfile.flush()
tmpfile_path = tmpfile.name
try:
with open(tmpfile_path, 'rb') as f:
result = subprocess.run(
['nc', '-w', '5', mvshost, str(RDRPORT)],
input=f.read(),
check=True,
capture_output=True
)
print(f"Deleted {dataset_name}({member_name})")
if result.stdout:
print("JES response:", result.stdout.decode(errors='ignore').strip())
return 0
except subprocess.CalledProcessError as e:
print(f"Deletion failed: {e}")
print("stderr:", e.stderr.decode(errors='ignore'))
return 1
finally:
os.unlink(tmpfile_path)
if __name__ == "__main__":
if len(sys.argv) < 2:
print("Usage: delete_mvs_member.py <pds_destination> [mvshost]")
print()
print("Arguments:")
print(" pds_destination - PDS destination as DATASET(MEMBER) (required)")
print(" mvshost - MVS host (optional, default: oldcomputernerd.com)")
print()
print("Examples:")
print(" delete_mvs_member.py '@05054.SRCLIB.C(SIEVE11)'")
print(" delete_mvs_member.py '@05054.SRCLIB.C(HELLO)' mainframe.example.com")
sys.exit(1)
destination = sys.argv[1]
# Parse PDS syntax: DATASET(MEMBER)
if '(' in destination and destination.endswith(')'):
dataset_name = destination[:destination.index('(')]
member_name = destination[destination.index('(')+1:-1]
else:
print(f"Error: Invalid PDS syntax '{destination}'. Use format: DATASET(MEMBER)")
sys.exit(1)
# Optional host override
mvshost = sys.argv[2] if len(sys.argv) > 2 else MVSHOST
print(f"Deleting: {dataset_name}({member_name})")
print(f"Host: {mvshost}")
print()
sys.exit(delete_member(dataset_name, member_name, mvshost))