Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
29bde65
adds batch command for quick generation en mass
sirugh Feb 25, 2025
52c9a80
add notes about rate limits
sirugh Feb 25, 2025
7b342c2
allow skipping git org creation
sirugh Feb 26, 2025
7456c5e
checkSync automation. create-repos script working. --skipGit flag wor…
sirugh Feb 27, 2025
883b753
f
sirugh Feb 27, 2025
e87bbba
adds deleterepos script for easy deletion assuming token with permiss…
sirugh Feb 27, 2025
5ba2918
f
sirugh Feb 27, 2025
6ca29dc
remove check for specific file and instead just trigger a code sync j…
sirugh Feb 27, 2025
7bfbded
Merge branch 'main' into batch-command
sirugh Feb 27, 2025
2e63f17
Merge branch 'main' into batch-command
sirugh Feb 27, 2025
f0ea534
Converted createRepos from command to script (#17)
revanth0212 Feb 27, 2025
d598a87
remove scripts from package
sirugh Feb 27, 2025
69e3193
pad repo to 2 digits. add args to createRepos
sirugh Feb 28, 2025
da90bf2
padding checkSync repo usage
sirugh Feb 28, 2025
6dcaccb
f
sirugh Feb 28, 2025
b2c42fa
remove jsdocs
sirugh Feb 28, 2025
dc00b32
f
sirugh Feb 28, 2025
ad15822
f
sirugh Feb 28, 2025
cf942e3
add auto-sync to restart the node process after the rate limit refresh
sirugh Feb 28, 2025
1b09f9c
5 second interval for processed check
sirugh Feb 28, 2025
89ac2ed
Merge branch 'main' into batch-command
sirugh Mar 7, 2025
279179b
allow empty datasource for empty string
sirugh Mar 7, 2025
f1380a3
additional
sirugh Mar 10, 2025
d58209a
Merge branch 'main' into batch-command
sirugh Mar 10, 2025
f889f34
use args instead of hardcoding source or destination
sirugh Mar 10, 2025
dfc08c0
add todo
sirugh Mar 10, 2025
d505690
Merge branch 'main' into batch-command
sirugh Mar 19, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 70 additions & 0 deletions src/scripts/autoSync.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
#!/bin/bash

# Function to get UTC time from progress.json and display it in CST
get_restart_time() {
local RESTART_AFTER=$(jq -r '.restartAfter' progress.json)
if [ "$RESTART_AFTER" != "0" ]; then
local RESTART_AFTER_SEC=$((RESTART_AFTER / 1000))
echo "Restart after: $(TZ='America/Chicago' date -r $RESTART_AFTER_SEC "+%Y-%m-%d %H:%M:%S") (CST)"
fi
}


# Function to check if condition is met
exit_if_all_checked() {
local LAST_CHECKED=$(jq -r '.lastChecked' progress.json)
if [ "$LAST_CHECKED" -gt "$END" ]; then
echo "All repos synced up to $END, quitting..."
exit 0
fi
}

# Function to format seconds into H:M:S
format_time() {
local SECONDS=$1
printf "%02d:%02d:%02d" $((SECONDS/3600)) $((SECONDS%3600/60)) $((SECONDS%60))
}

# Check if destination argument is provided
if [ $# -ne 3 ]; then
echo "Usage: ./autoSync.sh <destination> <start> <end>"
echo "Example: ./autoSync.sh adobe-summit-L322/seat 0 100"
exit 1
fi

DESTINATION=$1
START=$2
END=$3

# Validate arguments
if ! [[ "$START" =~ ^[0-9]+$ ]] || ! [[ "$END" =~ ^[0-9]+$ ]]; then
echo "Error: Start and End must be integers"
exit 1
fi

if [ "$START" -gt "$END" ]; then
echo "Error: Start cannot be greater than End"
exit 1
fi

# Main loop
while true; do
# Check condition to see if we should quit
exit_if_all_checked

# Get the current restart time and display it
get_restart_time

# Wait until restartAfter time has passed
RESTART_AFTER=$(jq -r '.restartAfter' progress.json)
RESTART_AFTER_SEC=$((RESTART_AFTER / 1000))
CURRENT_TIME=$(date +%s)
SLEEP_TIME=$((RESTART_AFTER_SEC - CURRENT_TIME))
if [ $SLEEP_TIME -gt 0 ]; then
echo "Sleeping for $(format_time $SLEEP_TIME)"
sleep $SLEEP_TIME
fi

# Run the script synchronously. When the script ends, it will write to progress.json with new restartAfter time
node src/scripts/checkSync.js "$DESTINATION" "$START" "$END"
done
160 changes: 160 additions & 0 deletions src/scripts/checkSync.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
/**
* This script is designed to check the status of code syncs for a series of repositories.
* Only to be used for housekeeping purposes. Please do not run this script without supervision.
*
* Usage: node checkSync.js <destination> <start> <end>
* Example: node checkSync.js adobe-summit-L322/seat 0 100
*/

import fs from 'fs'

const PROGRESS_FILE = 'progress.json'
const WAIT_TIME_MS = 1 * 60 * 60 * 1000 // 1 hour
// the amount of git files we expect the code sync to have to have processed.
// for L322, it seems to be 418. For L321, seems to be 412.
// TODO: this needs to be dynamic, based on the source repo.
const GIT_FILES_TO_CHECK = 412

function parseDestinationString (destString) {
const [org, prefix] = destString.split('/')
return { org, prefix }
}

function padIndex (index) {
return index.toString().padStart(2, '0')
}

function loadProgress () {
try {
if (fs.existsSync(PROGRESS_FILE)) {
const data = JSON.parse(fs.readFileSync(PROGRESS_FILE, 'utf8'))
return {
lastChecked: data.lastChecked === 0 ? 0 : data.lastChecked || 1,
restartAfter: data.restartAfter || 0
}
}
} catch (error) {
console.error('Error loading progress:', error)
}
return { lastChecked: 1, restartAfter: 0 } // Default values
}

function saveProgress (index, restartAfter = 0) {
try {
fs.writeFileSync(PROGRESS_FILE, JSON.stringify({ lastChecked: index, restartAfter }))
} catch (error) {
console.error('Error saving progress:', error)
}
}

async function checkRepo (destination, index) {
try {
await triggerCodeSync(destination, index)
console.log(`✅ Code sync completed for ${destination}-${padIndex(index)}. Proceeding to the next repo.`)
return index + 1 // Move to the next repo
} catch (error) {
console.error(`🚨 Error during code sync for ${destination}-${padIndex(index)}:`, error)
return index - 1 // Restart from the previous repo
}
}

async function saveAndExit (destination, index) {
// Determine restart time in CST
const restartTimestamp = Date.now() + WAIT_TIME_MS
const restartTime = new Date(restartTimestamp).toLocaleTimeString('en-US', { timeZone: 'America/Chicago', hour12: true })
console.log(`🚨 Script exiting. Restart at ${destination}-${padIndex(index)} after ~1 hour at: ${restartTime} CST`)

saveProgress(index, restartTimestamp)
process.exit(0)
}

async function triggerCodeSync (destination, seat) {
const { org, prefix } = parseDestinationString(destination)
const postUrl = `https://admin.hlx.page/code/${org}/${prefix}-${padIndex(seat)}/main/*`
console.log(`🔄 Calling Helix Admin Code Sync at: ${postUrl}`)

try {
const postResponse = await fetch(postUrl, { method: 'POST' })

if (postResponse.status !== 202) {
console.error(`No code found for ${destination}-${padIndex(seat)}. Ensure repo exists.`)
saveAndExit(destination, seat)
}
const postData = await postResponse.json()
const detailsUrl = `${postData.links.self}/details`
console.log(`🔍 Checking details at: ${detailsUrl}`)

await checkPhaseCompletion(detailsUrl, destination, seat)
} catch (error) {
console.error(`Error processing ${destination}-${padIndex(seat)}:`, error)
throw error
}
}

async function checkPhaseCompletion (detailsUrl, destination, seat, maxRetries = 60, interval = 5000) {
let attempts = 0

while (attempts < maxRetries) {
try {
const detailsResponse = await fetch(detailsUrl)
const detailsData = await detailsResponse.json()
if (detailsData.state === 'stopped') {
if (detailsData.progress && detailsData?.progress.processed >= GIT_FILES_TO_CHECK) {
console.log(`✅ ${destination}-${padIndex(seat)} processing completed.`)
return
} else if (detailsData.error.includes('rate limit exceeded')) {
console.log(`⚠️ ${destination}-${padIndex(seat)} processing rate limit exceeded.`)
saveAndExit(destination, seat)
} else {
console.log('Unknown state! Data:', detailsData)
saveAndExit(destination, seat)
}
} else {
console.log(`⏳ ${destination}-${padIndex(seat)} still processing... retrying (${attempts + 1}/${maxRetries}) in ${(interval / 1000)}s.`)
}
} catch (error) {
console.error(`Error fetching details for ${destination}-${padIndex(seat)}:`, error)
}

attempts++
await new Promise(resolve => setTimeout(resolve, interval)) // Wait 1s before retrying
}

await saveAndExit(destination, seat)
}

async function runChecks (destination, start, end) {
const { lastChecked, restartAfter } = loadProgress()

// Check if we are running before the allowed restart time
if (Date.now() < restartAfter) {
const restartTime = new Date(restartAfter).toLocaleTimeString('en-US', { timeZone: 'America/Chicago', hour12: true })
console.error(`❌ Cannot start yet. Please wait until: ${restartTime} CST.`)
process.exit(1)
}

let index = lastChecked
while (index <= end) {
index = await checkRepo(destination, index)
if (index < start) index = start // Prevent going below start
saveProgress(index)
}
console.log('✅ All repos checked successfully.')
}

if (process.argv.length !== 5) {
console.log('Usage: node checkSync.js <destination> <start> <end>')
console.log('Example: node checkSync.js adobe-summit-L322/seat 0 100')
} else {
const destination = process.argv[2]
const start = parseInt(process.argv[3])
const end = parseInt(process.argv[4])

if (!Number.isInteger(start) || !Number.isInteger(end)) {
console.error('Start and End must be integers')
} else if (start > end) {
console.log('Start cannot be greater than End.')
} else {
runChecks(destination, start, end)
}
}
65 changes: 65 additions & 0 deletions src/scripts/cloneContent.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/**
* This is a script to clone content automatically.
* Only to be used for housekeeping purposes. Please do not run this script without supervision.
*
* Usage: node cloneContent.js <source> <destination> <start> <end>
* Example: node cloneContent.js adobe-commerce/adobe-demo-store adobe-summit-L322/seat 0 100
*/

import { execSync } from 'child_process'

function parseRepoString (repoString) {
const [org, repo] = repoString.split('/')
return { org, repo }
}

function parseDestinationString (destString) {
const [org, prefix] = destString.split('/')
return { org, prefix }
}

async function cloneContent (source, destination, start, end) {
console.log('Starting to clone content...')
console.log(`Source: ${source}`)
console.log(`Destination: ${destination}`)

const { org: sourceOrg, repo: sourceRepo } = parseRepoString(source)
const { org: destOrg, prefix: repoPrefix } = parseDestinationString(destination)

for (let i = start; i <= end; i++) {
const repoNumber = i.toString().padStart(2, '0')
const command = `aio commerce:init --template "${sourceOrg}/${sourceRepo}" --repo "${destOrg}/${repoPrefix}-${repoNumber}" --datasource "" --skipMesh --skipGit`
console.log(`\nExecuting command ${i} of ${end}:`)
console.log(command)

try {
execSync(command, { stdio: 'inherit' })
console.log(`Successfully completed iteration ${i}`)
} catch (error) {
console.error(`Error in iteration ${i}:`, error.message)
throw error
}
}
}

if (process.argv.length !== 6) {
console.log('Usage: node cloneContent.js <source> <destination> <start> <end>')
console.log('Example: node cloneContent.js adobe-commerce/adobe-demo-store adobe-summit-L322/seat 0 100')
} else {
const source = process.argv[2]
const destination = process.argv[3]
const start = parseInt(process.argv[4])
const end = parseInt(process.argv[5])

if (!Number.isInteger(start) || !Number.isInteger(end)) {
console.error('Start and End must be integers')
} else if (start > end) {
console.log('Start cannot be greater than End.')
} else {
cloneContent(source, destination, start, end).then(() => {
console.log('All repos created successfully.')
}).catch((error) => {
console.error('Error creating repos:', error)
})
}
}
60 changes: 60 additions & 0 deletions src/scripts/createRepos.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/**
* This is a script to create multiple GitHub repositories using a template repository.
* Only to be used for housekeeping purposes. Please do not run this script without supervision.
*
* Usage: node createRepos.js <source> <destination> <start> <end>
* Example: node createRepos.js adobe-commerce/adobe-demo-store adobe-summit-L322/seat 0 100
*/

import { createRepo } from '../utils/github.js'

function parseRepoString (repoString) {
const [org, repo] = repoString.split('/')
return { org, repo }
}

function parseDestinationString (destString) {
const [org, prefix] = destString.split('/')
return { org, prefix }
}

async function createRepos (source, destination, start, end) {
console.log('Starting to create repos...')
console.log(`Source: ${source}`)
console.log(`Destination: ${destination}`)

const { org: sourceOrg, repo: sourceRepo } = parseRepoString(source)
const { org: destOrg, prefix: repoPrefix } = parseDestinationString(destination)

for (let i = start; i <= end; i++) {
const repo = `${repoPrefix}-${i.toString().padStart(2, '0')}`
try {
await createRepo(destOrg, repo, sourceOrg, sourceRepo)
} catch (e) {
console.error(`! Failed to complete run for "${repo}". Skipping.`)
console.error(e)
}
}
}

if (process.argv.length !== 6) {
console.log('Usage: node createRepos.js <source> <destination> <start> <end>')
console.log('Example: node createRepos.js adobe-commerce/adobe-demo-store adobe-summit-L322/seat 0 100')
} else {
const source = process.argv[2]
const destination = process.argv[3]
const start = parseInt(process.argv[4])
const end = parseInt(process.argv[5])

if (!Number.isInteger(start) || !Number.isInteger(end)) {
console.error('Start and End must be integers')
} else if (start > end) {
console.log('Start cannot be greater than End.')
} else {
createRepos(source, destination, start, end).then(() => {
console.log('All repos created successfully.')
}).catch((error) => {
console.error('Error creating repos:', error)
})
}
}
Loading