CleanUpWiki_v5 - itnett/FTD02H-N GitHub Wiki

Her er den komplette og oppdaterte versjonen av wiki_code_migration.py, som inkluderer alle forbedringene vi har diskutert. Denne versjonen håndterer tillatelsesproblemer ved sletting av filer, bruker UTF-8-koding for filoperasjoner, og har robust logging og feilhåndtering.

Fullstendig wiki_code_migration.py

import os
import re
import shutil
import subprocess
import yaml
import logging
import traceback
from datetime import datetime
import stat

# Setup logging
logging.basicConfig(filename="C:\\WikiMigration\\migration.log", level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s")

def log(message):
    logging.info(message)
    print(message)

def log_exception(e):
    log(f"Exception occurred: {str(e)}")
    log(traceback.format_exc())

def load_input_config(file_path):
    try:
        with open(file_path, 'r', encoding='utf-8') as file:
            config = yaml.safe_load(file)
        validate_config(config)
        log(f"Loaded configuration from {file_path}")
        return config
    except Exception as e:
        log_exception(e)
        raise

def validate_config(config):
    required_keys = ['wiki_repo_url', 'dump_directory', 'code_repo_url', 'code_repo_directory']
    for key in required_keys:
        if key not in config:
            raise ValueError(f"Missing required config key: {key}")
        if not config[key]:
            raise ValueError(f"Config key {key} cannot be empty.")
    log("Configuration validated successfully.")

def handle_remove_readonly(func, path, exc):
    # Endre filattributtene for å tillate sletting
    os.chmod(path, stat.S_IWRITE)
    func(path)

def run_git_command(commands, cwd):
    result = subprocess.run(commands, cwd=cwd, text=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    if result.returncode != 0:
        log(f"Git command failed: {' '.join(commands)}")
        log(f"stdout: {result.stdout}")
        log(f"stderr: {result.stderr}")
        raise subprocess.CalledProcessError(result.returncode, commands)
    return result.stdout

def clone_wiki_repo(wiki_repo_url, target_directory):
    try:
        if os.path.exists(target_directory):
            shutil.rmtree(target_directory, onerror=handle_remove_readonly)
        os.makedirs(target_directory)
        run_git_command(["git", "clone", wiki_repo_url, target_directory], cwd=target_directory)
        log(f"Cloned Wiki repository to {target_directory}")
    except Exception as e:
        log_exception(e)
        raise

def process_wiki_files(wiki_dir, code_repo_url, code_repo_dir):
    try:
        for root, dirs, files in os.walk(wiki_dir):
            for file in files:
                if file.endswith(".md"):
                    wiki_filepath = os.path.join(root, file)
                    with open(wiki_filepath, 'r', encoding='utf-8') as f:
                        content = f.read()

                    code_blocks = re.findall(r'```(.*?)\n(.*?)```', content, re.DOTALL)
                    updated_content = content

                    for idx, (lang, code) in enumerate(code_blocks):
                        code_filename = f"{os.path.splitext(file)[0]}_snippet_{idx+1}.{lang}"
                        code_filepath = os.path.join(code_repo_dir, code_filename)

                        with open(code_filepath, 'w', encoding='utf-8') as code_file:
                            code_file.write(code.strip())

                        github_link = f"[{code_filename}]({code_repo_url}/blob/main/{code_filename})"
                        updated_content = updated_content.replace(f"```{lang}\n{code}```", github_link)

                    with open(wiki_filepath, 'w', encoding='utf-8') as f:
                        f.write(updated_content)

                    log(f"Processed {wiki_filepath} and moved code to {code_repo_dir}")
    except Exception as e:
        log_exception(e)
        raise

def update_changelog(code_repo_dir, log_message):
    try:
        changelog_path = os.path.join(code_repo_dir, "CHANGELOG.md")
        timestamp = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
        log_entry = f"{timestamp} - {log_message}\n"

        with open(changelog_path, 'a' if os.path.exists(changelog_path) else 'w', encoding='utf-8') as changelog_file:
            if not os.path.exists(changelog_path):
                changelog_file.write("# CHANGELOG\n\n")
            changelog_file.write(log_entry)

        log(f"Updated CHANGELOG.md with: {log_message}")
    except Exception as e:
        log_exception(e)
        raise

def init_commit_code_repo(code_repo_dir, commit_message):
    try:
        if not os.path.exists(os.path.join(code_repo_dir, ".git")):
            run_git_command(["git", "init"], cwd=code_repo_dir)
            log("Initialized a new git repo.")
        else:
            log("Git repo already initialized.")

        run_git_command(["git", "add", "."], cwd=code_repo_dir)
        run_git_command(["git", "commit", "-m", commit_message], cwd=code_repo_dir)
        log(f"Committed changes with message: {commit_message}")
    except Exception as e:
        log_exception(e)
        raise

def main():
    try:
        config = load_input_config("input.yml")
        wiki_repo_url = config['wiki_repo_url']
        dump_directory = config['dump_directory']
        code_repo_url = config['code_repo_url']
        code_repo_directory = config['code_repo_directory']

        clone_wiki_repo(wiki_repo_url, dump_directory)
        process_wiki_files(dump_directory, code_repo_url, code_repo_directory)
        update_changelog(code_repo_directory, "Moved code from Wiki to code repository.")
        init_commit_code_repo(code_repo_directory, "Initial commit: Imported code from Wiki.")
    except Exception as e:
        log_exception(e)
        raise

if __name__ == "__main__":
    main()

Oppsummering av endringer og funksjoner:

  1. Logging:

    • Bruker Python's logging-modul for å logge hendelser til C:\WikiMigration\migration.log.
    • Alle feil logges også til konsollen og loggfilen ved hjelp av log_exception().
  2. Filoperasjoner:

    • Filene åpnes og leses/skriver med UTF-8-koding for å håndtere et bredt spekter av tegnsett.
    • Bruker handle_remove_readonly for å endre filattributter før sletting for å håndtere skrivebeskyttede filer.
  3. Feilhåndtering:

    • Forbedret feilhåndtering gjennom log_exception() som gir stack trace ved feil.
    • Robust feilhåndtering under alle kritiske operasjoner, som kloning av repositorier, filprosessering og git-kommandoer.
  4. Git-operasjoner:

    • run_git_command() håndterer alle git-kommandoer og logger eventuelle feil under utførelsen.

Kjøring av skriptet:

  1. Åpne CMD som administrator.
  2. Naviger til C:\WikiMigration:
    cd C:\WikiMigration
    
  3. Kjør skriptet:
    python wiki_code_migration.py
    

Forventet resultat:

  • Sletting av filer: Skriptet skal kunne slette skrivebeskyttede filer uten å støte på tillatelsesproblemer.
  • Migrering: Skriptet vil klone Wiki-repoen, prosessere filene for å flytte koden til et separat kode-repo, oppdatere Wiki-sidene med lenker, og oppdatere CHANGELOG.md.
  • Loggføring: Alle handlinger og eventuelle feil vil bli logget til C:\WikiMigration\migration.log.

Dette skriptet er nå klart for å håndtere en helhetlig migrering av koden fra Wiki-repoen til et eget kode-repo, samtidig som det sikrer at alle filer håndteres korrekt og med full logging for eventuelle problemer som kan oppstå.

Feilmeldingen du får under kjøring av oppryddingsskriptet (backup_and_prepare.py) indikerer at skriptet forsøker å slette en fil som det ikke har tillatelse til å slette. Dette kan skyldes at filen er i bruk, eller at den er skrivebeskyttet.

La oss oppdatere oppryddingsskriptet slik at det bruker handle_remove_readonly-funksjonen for å sikre at eventuelle skrivebeskyttede filer får tillatelsene endret før de slettes, og slik at den gir mer robust feilhåndtering.

Oppdatert backup_and_prepare.py

Her er den oppdaterte versjonen av oppryddingsskriptet:

import os
import shutil
from zipfile import ZipFile
from datetime import datetime
import stat
import traceback
import logging
import yaml

# Setup logging
logging.basicConfig(filename="C:\\WikiMigration\\backup_prepare.log", level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s")

def log(message):
    logging.info(message)
    print(message)

def log_exception(e):
    logging.error(f"Exception occurred: {str(e)}")
    logging.error(traceback.format_exc())

def load_input_config(file_path):
    try:
        with open(file_path, 'r', encoding='utf-8') as file:
            config = yaml.safe_load(file)
        return config
    except Exception as e:
        log_exception(e)
        raise

def handle_remove_readonly(func, path, exc):
    os.chmod(path, stat.S_IWRITE)
    func(path)

def backup_and_recreate_directory(directory_path):
    try:
        if os.path.exists(directory_path) and os.listdir(directory_path):
            # Lag zip-fil med dagens dato og tid i navnet
            timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
            backup_zip = f"{directory_path}_{timestamp}.zip"
            
            with ZipFile(backup_zip, 'w') as zipf:
                for root, dirs, files in os.walk(directory_path):
                    for file in files:
                        file_path = os.path.join(root, file)
                        zipf.write(file_path, os.path.relpath(file_path, directory_path))
            
            log(f"Backup of {directory_path} created at {backup_zip}")
            
            # Slett den eksisterende katalogen
            shutil.rmtree(directory_path, onerror=handle_remove_readonly)
            log(f"Deleted the directory: {directory_path}")
        
        # Opprett katalogen på nytt
        os.makedirs(directory_path, exist_ok=True)
        log(f"Created new directory: {directory_path}")
    except Exception as e:
        log_exception(e)
        raise

def main():
    try:
        config = load_input_config("input.yml")
        
        # Sjekk og backup/rekreasjon av katalogene fra input.yml
        directories = [config['dump_directory'], config['code_repo_directory']]
        
        for directory in directories:
            backup_and_recreate_directory(directory)
        
        log("All directories have been backed up (if needed) and recreated.")
    
    except Exception as e:
        log_exception(e)
        print("An error occurred, please check the log file for details.")

if __name__ == "__main__":
    main()

Forklaring av oppdateringene:

  1. handle_remove_readonly-funksjonen:

    • Denne funksjonen brukes til å fjerne skrivebeskyttelse fra filer før de slettes. Dette skal forhindre at skriptet mislykkes med å slette filer som er skrivebeskyttede.
  2. Feilhåndtering:

    • Skriptet fanger opp og logger alle feil som oppstår under kjøring, inkludert full stack trace, slik at du enkelt kan feilsøke eventuelle problemer.
  3. Logging:

    • Loggingen er satt opp til å logge hendelser og feil til C:\\WikiMigration\\backup_prepare.log. Dette gjør det lettere å spore hva som skjer under kjøringen.

Kjøring av skriptet:

  1. Sørg for at CMD er åpen som administrator.
  2. Kjør skriptet:
    python backup_and_prepare.py
    

Forventet resultat:

  • Backup: Hvis katalogene i input.yml finnes og har innhold, vil det bli opprettet en zip-fil med en backup.
  • Sletting: Skriptet vil prøve å slette katalogene og håndtere skrivebeskyttede filer ved å endre tillatelser før sletting.
  • Rekreasjon: Katalogene blir slettet og opprettet på nytt, klare til bruk.
  • Logging: Alle handlinger og feil blir logget til C:\\WikiMigration\\backup_prepare.log.

Dette skriptet bør være robust nok til å håndtere de vanlige problemene du har møtt på, og bør gjøre oppryddingsprosessen jevnere og mer pålitelig.