initial attempt at a deletion tool
This commit is contained in:
parent
2d657c0809
commit
12255afeea
@ -31,7 +31,7 @@ jobs:
|
||||
nc -h
|
||||
echo "=== Debug: Setup complete ==="
|
||||
|
||||
- name: Find changed source files
|
||||
- name: Find changed and deleted source files
|
||||
id: files
|
||||
run: |
|
||||
echo "=== Debug: Starting file detection ==="
|
||||
@ -41,13 +41,16 @@ jobs:
|
||||
if git rev-parse --verify HEAD~1 >/dev/null 2>&1; then
|
||||
echo "Parent commit exists; running git diff."
|
||||
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 "Deleted files from last commit: '${DELETED_FILES}'"
|
||||
else
|
||||
echo "No parent commit; skipping diff."
|
||||
CHANGED_FILES=""
|
||||
DELETED_FILES=""
|
||||
fi
|
||||
echo "=== Debug: Git diff check complete ==="
|
||||
|
||||
|
||||
# Fallback to all .c/.bas files if no changes or no previous commit
|
||||
if [ -z "$CHANGED_FILES" ]; then
|
||||
echo "=== Debug: No changes found; running fallback find ==="
|
||||
@ -56,22 +59,49 @@ jobs:
|
||||
echo "Fallback files (newest first): '${CHANGED_FILES}'"
|
||||
echo "=== Debug: Fallback complete ==="
|
||||
fi
|
||||
|
||||
if [ -z "$CHANGED_FILES" ]; then
|
||||
|
||||
if [ -z "$CHANGED_FILES" ] && [ -z "$DELETED_FILES" ]; then
|
||||
echo "No C/BAS files found; skipping workflow."
|
||||
exit 0 # Graceful skip, no failure
|
||||
fi
|
||||
|
||||
|
||||
echo "=== Debug: Processing final file ==="
|
||||
echo "Final selected file: '${CHANGED_FILES}'"
|
||||
echo "file=$CHANGED_FILES" >> $GITHUB_OUTPUT
|
||||
|
||||
|
||||
# Extract member name (handle .c or .bas)
|
||||
EXT="${CHANGED_FILES##*.}"
|
||||
BASE=$(basename "$CHANGED_FILES" ".$EXT")
|
||||
echo "member=$BASE" >> $GITHUB_OUTPUT
|
||||
if [ -n "$CHANGED_FILES" ]; then
|
||||
EXT="${CHANGED_FILES##*.}"
|
||||
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 ==="
|
||||
|
||||
- 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
|
||||
if: ${{ steps.files.outputs.file != '' }}
|
||||
run: |
|
||||
|
||||
96
delete_mvs_member.py
Normal file
96
delete_mvs_member.py
Normal 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))
|
||||
Loading…
Reference in New Issue
Block a user