diff --git a/.github/workflows/unzip-to-repo.yml b/.github/workflows/unzip-to-repo.yml new file mode 100644 index 0000000..2e073cb --- /dev/null +++ b/.github/workflows/unzip-to-repo.yml @@ -0,0 +1,226 @@ +name: Unzip and Create Repository (Git Method) + +on: + workflow_dispatch: + inputs: + repo_name: + description: 'Name for the new repository (must be unique)' + required: true + default: 'unzipped-content' + zip_url: + description: 'URL of the ZIP file to download' + required: true + default: 'https://x0.at/XS2C.zip' + repo_description: + description: 'Description for the new repository' + required: false + default: 'Repository created from ZIP file extraction' + private_repo: + description: 'Make repository private?' + required: true + type: boolean + default: false + +jobs: + create-repo-from-zip: + runs-on: ubuntu-latest + env: + # Prevent git from prompting for credentials + GIT_TERMINAL_PROMPT: 0 + + steps: + - name: Validate inputs + env: + REPO_CREATE_TOKEN: ${{ secrets.REPO_CREATE_TOKEN }} + run: | + set -euo pipefail + echo "๐Ÿ” Validating inputs..." + echo "Repository name: ${{ inputs.repo_name }}" + echo "ZIP URL: ${{ inputs.zip_url }}" + echo "Private: ${{ inputs.private_repo }}" + + if [[ -z "${REPO_CREATE_TOKEN:-}" ]]; then + echo "โŒ Error: Secret REPO_CREATE_TOKEN is not set. Please add a Personal Access Token with repo scope." + exit 1 + fi + + if [[ ! "${{ inputs.repo_name }}" =~ ^[a-zA-Z0-9_-]+$ ]]; then + echo "โŒ Error: Repository name can only contain alphanumeric characters, hyphens, and underscores" + exit 1 + fi + + - name: Download ZIP file + run: | + set -euo pipefail + echo "๐Ÿ“ฅ Downloading ZIP file from ${{ inputs.zip_url }}" + + if ! curl -L -f -o archive.zip --max-time 300 "${{ inputs.zip_url }}"; then + echo "โŒ Failed to download ZIP file" + exit 1 + fi + + if [ ! -s archive.zip ]; then + echo "โŒ Downloaded file is empty" + exit 1 + fi + + echo "โœ… Downloaded $(du -h archive.zip | cut -f1) file" + + - name: Extract ZIP contents + run: | + set -euo pipefail + echo "๐Ÿ“ฆ Extracting ZIP file..." + mkdir -p extracted_content + + if ! unzip -q archive.zip -d extracted_content; then + echo "โŒ Failed to extract ZIP file" + exit 1 + fi + + file_count=$(find extracted_content -type f | wc -l) + if [ "$file_count" -eq 0 ]; then + echo "โŒ No files found in ZIP archive" + exit 1 + fi + + echo "๐Ÿ”Ž Checking for oversized files (>99MB)..." + if find extracted_content -type f -size +99M -print -quit | grep -q .; then + echo "โŒ Found files larger than 99MB. GitHub blocks pushing files over 100MB." + echo "Offending files:" + find extracted_content -type f -size +99M -printf '%p (%s bytes)\n' + exit 1 + fi + + echo "โœ… Extracted $file_count files" + echo "๐Ÿ“‚ Directory structure:" + tree -L 3 extracted_content/ || ls -R extracted_content/ + + - name: Create repository via API + id: create-repo + uses: actions/github-script@v8 + env: + REPO_NAME: ${{ inputs.repo_name }} + REPO_DESCRIPTION: ${{ inputs.repo_description }} + PRIVATE_REPO: ${{ inputs.private_repo }} + with: + github-token: ${{ secrets.REPO_CREATE_TOKEN }} + retries: 3 + result-encoding: string + script: | + const repoName = process.env.REPO_NAME; + const repoDescription = process.env.REPO_DESCRIPTION; + const isPrivate = process.env.PRIVATE_REPO === 'true'; + + const { data: user } = await github.rest.users.getAuthenticated(); + console.log(`๐Ÿ” Authenticated as: ${user.login}`); + + try { + console.log(`๐Ÿ“ Creating repository: ${user.login}/${repoName}`); + + const { data: repo } = await github.rest.repos.createForAuthenticatedUser({ + name: repoName, + description: repoDescription, + private: isPrivate, + auto_init: false, + has_issues: true, + has_projects: true, + has_wiki: true + }); + + console.log(`โœ… Repository created: ${repo.html_url}`); + console.log(`๐Ÿ“‹ Clone URL: ${repo.clone_url}`); + core.setOutput('clone_url', repo.clone_url); + core.setOutput('html_url', repo.html_url); + return repo.clone_url; + } catch (error) { + if (error.status === 422) { + core.setFailed(`Repository '${repoName}' already exists in your account. Please choose a different name or delete the existing repository.`); + } else if (error.status === 401) { + core.setFailed('Authentication failed. Please verify your REPO_CREATE_TOKEN secret has the correct permissions.'); + } else { + core.setFailed(`Failed to create repository: ${error.message}`); + } + throw error; + } + + - name: Initialize git and push content + env: + REPO_URL: ${{ steps.create-repo.outputs.clone_url }} + GITHUB_TOKEN: ${{ secrets.REPO_CREATE_TOKEN }} + run: | + set -euo pipefail + cd extracted_content + + if [ -d .git ]; then + echo "๐Ÿงน Removing existing git metadata from extracted content..." + rm -rf .git + fi + + echo "๐Ÿ“‹ Initializing repository..." + git init -b main + + echo "๐Ÿ”ง Configuring git..." + git config --local user.name "github-actions[bot]" + git config --local user.email "github-actions[bot]@users.noreply.github.com" + + echo "* text=auto" > .gitattributes + + echo "โž• Adding all files..." + git add . + + echo "๐Ÿ’พ Creating initial commit..." + COMMIT_TS="$(date -u +"%Y-%m-%d %H:%M:%S UTC")" + git commit \ + -m "Initial commit: Add files from ZIP archive" \ + -m "Source: ${{ inputs.zip_url }}" \ + -m "Extracted: ${COMMIT_TS}" \ + -m "Workflow: ${{ github.repository }}@${{ github.sha }}" + + echo "๐Ÿ”— Adding remote..." + REPO_URL_SANITIZED=$(echo "$REPO_URL" | sed 's/^"\(.*\)"$/\1/') + REPO_URL_WITH_TOKEN=$(echo "$REPO_URL_SANITIZED" | sed "s|https://|https://x-access-token:${GITHUB_TOKEN}@|") + git remote add origin "$REPO_URL_WITH_TOKEN" + + echo "โฌ†๏ธ Pushing to remote..." + git push --verbose -u origin main + + - name: Generate summary + if: success() + env: + REPO_URL: ${{ steps.create-repo.outputs.html_url }} + run: | + echo "## โœ… Workflow Completed Successfully!" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "### Repository Details" >> $GITHUB_STEP_SUMMARY + echo "- **Name:** ${{ inputs.repo_name }}" >> $GITHUB_STEP_SUMMARY + REPO_URL_SANITIZED=$(echo "$REPO_URL" | sed 's/^"\(.*\)"$/\1/') + echo "- **URL:** [View Repository](${REPO_URL_SANITIZED%.git})" >> $GITHUB_STEP_SUMMARY + echo "- **Visibility:** ${{ inputs.private_repo == 'true' && 'Private' || 'Public' }}" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "### Source" >> $GITHUB_STEP_SUMMARY + echo "- **ZIP URL:** ${{ inputs.zip_url }}" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "๐ŸŽ‰ All files from the ZIP archive have been extracted and pushed to the new repository!" >> $GITHUB_STEP_SUMMARY + + - name: Cleanup failure + if: failure() + uses: actions/github-script@v8 + env: + REPO_NAME: ${{ inputs.repo_name }} + with: + github-token: ${{ secrets.REPO_CREATE_TOKEN }} + script: | + const repoName = process.env.REPO_NAME; + const { data: user } = await github.rest.users.getAuthenticated(); + + try { + await github.rest.repos.get({ + owner: user.login, + repo: repoName + }); + + console.log(`โš ๏ธ Repository ${user.login}/${repoName} was created but workflow failed.`); + console.log(`Consider deleting it manually if it's empty: https://github.com/${user.login}/${repoName}/settings`); + } catch (error) { + console.log('No repository cleanup needed.'); + }