|
| 1 | +const core = require('@actions/core'); |
| 2 | +const github = require('@actions/github'); |
| 3 | +const {exec} = require('child_process'); |
| 4 | +const fs = require('fs'); |
| 5 | +const path = require('path'); |
| 6 | +const util = require('util'); |
| 7 | +const execAsync = util.promisify(exec); |
| 8 | +const SUBMODULE_PATH = 'meta/attributes/public'; |
| 9 | +const TEMP_DIR = process.env.TEMP_DIR || 'temp_submodule'; |
| 10 | +const PHP_FILES_DIR = process.env.PHP_FILES_DIR || 'filtered_submodule'; |
| 11 | + |
| 12 | +async function run() { |
| 13 | + try { |
| 14 | + const { token, gitUserName, gitUserEmail } = validateInputs(); |
| 15 | + const octokit = github.getOctokit(token); |
| 16 | + const context = github.context; |
| 17 | + const tagName = await getTagName(context.ref); |
| 18 | + const releaseName = `PhpStorm ${tagName.replace('v', '')}`; |
| 19 | + |
| 20 | + await configureGit(gitUserName, gitUserEmail); |
| 21 | + |
| 22 | + try { |
| 23 | + await createTemporaryBranch(); |
| 24 | + await manageSubmoduleFiles(TEMP_DIR, PHP_FILES_DIR); |
| 25 | + } finally { |
| 26 | + core.info('Cleaning up temporary directories...'); |
| 27 | + await cleanupDirs([TEMP_DIR, PHP_FILES_DIR]); |
| 28 | + } |
| 29 | + |
| 30 | + await commitAndPushChanges(tagName); |
| 31 | + |
| 32 | + await createGithubRelease(octokit, tagName, releaseName, context); |
| 33 | + |
| 34 | + } catch (error) { |
| 35 | + core.error(`Run failed: ${error.message}`); |
| 36 | + core.setFailed(error.message); |
| 37 | + } |
| 38 | +} |
| 39 | + |
| 40 | +// Top-level error handling |
| 41 | +run().catch(error => { |
| 42 | + core.error(`Unhandled error: ${error.message}`); |
| 43 | + core.setFailed(error.message); |
| 44 | +}); |
| 45 | + |
| 46 | +/** |
| 47 | + * @param {string} dir - The directory to start reading from. |
| 48 | + * @returns {Array<string>} - A flat list of all file paths. |
| 49 | + */ |
| 50 | +async function readDirRecursively(dir) { |
| 51 | + try { |
| 52 | + const entries = await fs.promises.readdir(dir, { withFileTypes: true }); |
| 53 | + const files = await Promise.all(entries.map(async (entry) => { |
| 54 | + const fullPath = path.join(dir, entry.name); |
| 55 | + return entry.isDirectory() ? await readDirRecursively(fullPath) : fullPath; |
| 56 | + })); |
| 57 | + return files.flat(); |
| 58 | + } catch (error) { |
| 59 | + core.error(`Error reading directory ${dir}: ${error.message}`); |
| 60 | + throw error; |
| 61 | + } |
| 62 | +} |
| 63 | + |
| 64 | + |
| 65 | +async function configureGit(gitUserName, gitUserEmail) { |
| 66 | + core.info('Configuring Git...'); |
| 67 | + try { |
| 68 | + await safeExec(`git config --global user.name "${gitUserName}"`); |
| 69 | + await safeExec(`git config --global user.email "${gitUserEmail}"`); |
| 70 | + core.info(`Git configured successfully with user: ${gitUserName}, email: ${gitUserEmail}`); |
| 71 | + } catch (error) { |
| 72 | + core.error('Failed to configure Git.'); |
| 73 | + core.setFailed(error.message); |
| 74 | + throw error; |
| 75 | + } |
| 76 | +} |
| 77 | + |
| 78 | +async function manageSubmoduleFiles(tempDir, phpFilesDir) { |
| 79 | + core.info('Initializing and updating submodule...'); |
| 80 | + await safeExec('git submodule update --init --recursive'); |
| 81 | + |
| 82 | + core.info('Saving submodule files...'); |
| 83 | + await createDir(tempDir) |
| 84 | + await createDir(phpFilesDir); |
| 85 | + await safeExec(`cp -r ${SUBMODULE_PATH}/* ${tempDir}`); |
| 86 | + |
| 87 | + await copyPhpFiles(tempDir, phpFilesDir); |
| 88 | + |
| 89 | + /*core.info(`Reading contents of ${tempDir} recursively...`); |
| 90 | + const allFiles = await readDirRecursively(tempDir); |
| 91 | + core.info(`Files read: ${allFiles.length}`); |
| 92 | +
|
| 93 | + core.info('Filtering PHP files...'); |
| 94 | + allFiles.forEach(filePath => { |
| 95 | + core.info(`Processing file: ${filePath}`); |
| 96 | + if (filePath.endsWith('.php')) { |
| 97 | + const fileName = path.basename(filePath); |
| 98 | + const destPath = path.join(phpFilesDir, fileName); |
| 99 | + fs.copyFileSync(filePath, destPath); |
| 100 | + } |
| 101 | + }); |
| 102 | +
|
| 103 | + if (fs.readdirSync(phpFilesDir).length === 0) { |
| 104 | + core.info('No PHP files found during filtering.'); |
| 105 | + } else { |
| 106 | + core.info('PHP files successfully filtered and copied.'); |
| 107 | + }*/ |
| 108 | + |
| 109 | + core.info('Removing submodule...'); |
| 110 | + await safeExec(`git submodule deinit -f -- ${SUBMODULE_PATH}`); |
| 111 | + await safeExec(`git rm -f ${SUBMODULE_PATH}`); |
| 112 | + await safeExec(`rm -rf .git/modules/${SUBMODULE_PATH}`); |
| 113 | + |
| 114 | + core.info('Restoring filtered PHP files...'); |
| 115 | + await fs.promises.mkdir(`${SUBMODULE_PATH}`, { recursive: true }); |
| 116 | + await safeExec(`cp -r ${phpFilesDir}/* ${SUBMODULE_PATH}`); |
| 117 | +} |
| 118 | + |
| 119 | +async function copyPhpFiles(sourceDir, destinationDir) { |
| 120 | + const phpFiles = []; |
| 121 | + const allFiles = await readDirRecursively(sourceDir); |
| 122 | + |
| 123 | + await Promise.all( |
| 124 | + allFiles.map(async (filePath) => { |
| 125 | + if (filePath.endsWith('.php')) { |
| 126 | + const fileName = path.basename(filePath); |
| 127 | + const destPath = path.join(destinationDir, fileName); |
| 128 | + phpFiles.push(filePath); |
| 129 | + await fs.promises.copyFile(filePath, destPath); |
| 130 | + } |
| 131 | + }) |
| 132 | + ); |
| 133 | + |
| 134 | + return phpFiles; |
| 135 | +} |
| 136 | + |
| 137 | +async function createTemporaryBranch() { |
| 138 | + const tempBranch = `release-${Date.now()}`; |
| 139 | + core.info(`Creating temporary branch ${tempBranch}...`); |
| 140 | + await safeExec(`git checkout -b ${tempBranch}`); |
| 141 | +} |
| 142 | + |
| 143 | +async function commitAndPushChanges(tagName) { |
| 144 | + core.info('Committing changes...'); |
| 145 | + await safeExec('git add -f ' + SUBMODULE_PATH); |
| 146 | + await safeExec('git commit -m "Convert submodule to regular files for release"'); |
| 147 | + |
| 148 | + core.info('Updating and pushing tag...'); |
| 149 | + await safeExec(`git tag -f ${tagName}`); |
| 150 | + await safeExec('git push origin --force --tags'); |
| 151 | +} |
| 152 | + |
| 153 | +async function getTagName(ref) { |
| 154 | + if (!ref.startsWith('refs/tags/')) { |
| 155 | + core.error(`Invalid ref: ${ref}. This action should be triggered by a tag push.`); |
| 156 | + throw new Error('This action expects a tag push event.'); |
| 157 | + } |
| 158 | + const tagName = ref.replace('refs/tags/', ''); |
| 159 | + core.info(`Tag identified: ${tagName}`); |
| 160 | + return tagName; |
| 161 | +} |
| 162 | + |
| 163 | +async function createGithubRelease(octokit, tagName, releaseName, context) { |
| 164 | + core.info(`Creating release ${releaseName} from tag ${tagName}...`); |
| 165 | + const release = await octokit.rest.repos.createRelease({ |
| 166 | + ...context.repo, |
| 167 | + tag_name: tagName, |
| 168 | + name: releaseName, |
| 169 | + body: 'Automated release including submodule files', |
| 170 | + draft: false, |
| 171 | + prerelease: false |
| 172 | + }); |
| 173 | + |
| 174 | + core.info('Release created successfully!'); |
| 175 | + core.setOutput('release-url', release.data.html_url); |
| 176 | +} |
| 177 | + |
| 178 | +async function cleanupDirs(directories) { |
| 179 | + try { |
| 180 | + await Promise.all( |
| 181 | + directories.map(async (directory) => { |
| 182 | + await fs.promises.rm(directory, { recursive: true, force: true }); |
| 183 | + core.info(`Successfully cleaned: ${directory}`); |
| 184 | + }) |
| 185 | + ); |
| 186 | + } catch (error) { |
| 187 | + core.warning(`Error during cleanup: ${error.message}`); |
| 188 | + } |
| 189 | +} |
| 190 | + |
| 191 | +function validateInputs() { |
| 192 | + const token = core.getInput('github-token', { required: true }); |
| 193 | + const gitUserName = core.getInput('git-user-name') || 'GitHub Action'; |
| 194 | + const gitUserEmail = core.getInput('git-user-email') || '[email protected]'; |
| 195 | + |
| 196 | + if (!token) { |
| 197 | + throw new Error('A valid GitHub Token is required to authenticate.'); |
| 198 | + } |
| 199 | + |
| 200 | + return { token, gitUserName, gitUserEmail }; |
| 201 | +} |
| 202 | + |
| 203 | +async function createDir(directory) { |
| 204 | + try { |
| 205 | + await fs.promises.mkdir(directory, { recursive: true }); |
| 206 | + core.info(`Directory created: ${directory}`); |
| 207 | + } catch (error) { |
| 208 | + core.error(`Failed to create directory: ${directory}`); |
| 209 | + throw error; |
| 210 | + } |
| 211 | +} |
| 212 | + |
| 213 | +async function safeExec(command) { |
| 214 | + try { |
| 215 | + const { stdout, stderr } = await execAsync(command); |
| 216 | + if (stderr) { |
| 217 | + core.warning(`Command warning: ${stderr}`); |
| 218 | + } |
| 219 | + return stdout.trim(); |
| 220 | + } catch (error) { |
| 221 | + core.error(`Command failed: ${command}`); |
| 222 | + core.error(`Error: ${error.message}`); |
| 223 | + throw error; |
| 224 | + } |
| 225 | +} |
0 commit comments