diff --git a/.github/workflows/update-plugins-repo-refs.yaml b/.github/workflows/update-plugins-repo-refs.yaml index fb6b8f4..1c324b0 100644 --- a/.github/workflows/update-plugins-repo-refs.yaml +++ b/.github/workflows/update-plugins-repo-refs.yaml @@ -572,6 +572,11 @@ jobs: core.setOutput('plugins-repo', workspace.repo); core.setOutput('plugins-repo-flat', workspace.flat); core.setOutput('plugin-directories', workspace.plugins.map((plugin) => plugin.directory).join('\n')); + const pluginVersions = {}; + for (const plugin of workspace.plugins) { + pluginVersions[plugin.name] = plugin.version; + } + core.setOutput('plugin-versions', JSON.stringify(pluginVersions)); return {}; - name: Create PR if necessary @@ -588,6 +593,7 @@ jobs: plugins-repo: ${{ steps.prepare.outputs.plugins-repo }} plugins-repo-flat: ${{ steps.prepare.outputs.plugins-repo-flat }} plugin-directories: ${{ steps.prepare.outputs.plugin-directories }} + plugin-versions: ${{ steps.prepare.outputs.plugin-versions }} allow-workspace-addition: ${{ inputs.allow-workspace-addition }} pr-to-update: ${{ inputs.pr-to-update }} diff --git a/update-overlay/action.yaml b/update-overlay/action.yaml index 003105a..6c3cc9c 100644 --- a/update-overlay/action.yaml +++ b/update-overlay/action.yaml @@ -50,6 +50,11 @@ inputs: required: false default: "" + plugin-versions: + description: "JSON object mapping packageName to version for metadata updates" + required: false + default: "{}" + runs: using: "composite" steps: @@ -69,6 +74,7 @@ runs: INPUT_PLUGIN_DIRECTORIES: ${{ inputs.plugin-directories }} INPUT_ALLOW_WORKSPACE_ADDITION: ${{ inputs.allow-workspace-addition }} INPUT_PR_TO_UPDATE: ${{ inputs.pr-to-update }} + INPUT_PLUGIN_VERSIONS: ${{ inputs.plugin-versions }} with: retries: 4 diff --git a/update-overlay/create-pr-if-necessary.js b/update-overlay/create-pr-if-necessary.js index 971107b..5eb8aef 100644 --- a/update-overlay/create-pr-if-necessary.js +++ b/update-overlay/create-pr-if-necessary.js @@ -13,6 +13,7 @@ module.exports = async ({github, context, core}) => { const pluginDirectories = core.getInput('plugin_directories'); const allowWorkspaceAddition = core.getInput('allow_workspace_addition'); const prToUpdate = core.getInput('pr_to_update'); + const pluginVersions = JSON.parse(core.getInput('plugin_versions') || '{}'); const updateCommitLabel = 'needs-commit-update'; @@ -134,6 +135,97 @@ module.exports = async ({github, context, core}) => { } } + /** + * Read metadata/*.yaml files from the overlay repo via GraphQL, + * update spec.version and spec.dynamicArtifact (oci://ghcr.io) to match + * the plugin versions discovered from NPM. + * @param {string} branchName + * @returns {Promise>} + */ + async function updateMetadataFiles(branchName) { + if (Object.keys(pluginVersions).length === 0) { + return []; + } + + /** @type {{ repository: { metadataTree: { entries: Array<{name: string, object: {text: string} | null}> } | null } }} */ + const metadataResponse = await github.graphql(` + query GetMetadataFiles($owner: String!, $repo: String!) { + repository(owner: $owner, name: $repo) { + metadataTree: object(expression: "${branchName}:${workspacePath}/metadata") { + ... on Tree { + entries { + name + object { + ... on Blob { + text + } + } + } + } + } + } + }`, { + owner: overlayRepoOwner, + repo: overlayRepoName + }); + + const entries = metadataResponse.repository?.metadataTree?.entries; + if (!entries) { + return []; + } + + /** @type {Array<{path: string, mode: string, content: string}>} */ + const treeEntries = []; + + for (const entry of entries) { + if (!entry.name.endsWith('.yaml') && !entry.name.endsWith('.yml')) continue; + if (!entry.object?.text) continue; + + let content = entry.object.text; + const packageNameMatch = content.match(/^\s+packageName:\s*"?([^"\n]+)"?\s*$/m); + if (!packageNameMatch) continue; + + const packageName = packageNameMatch[1].trim(); + const newVersion = pluginVersions[packageName]; + if (!newVersion) continue; + + let modified = false; + + // Update spec.version + const versionRegex = /^(\s+version:\s*).+$/m; + const versionMatch = content.match(versionRegex); + if (versionMatch && versionMatch[0].trim() !== `version: ${newVersion}`) { + content = content.replace(versionRegex, `$1${newVersion}`); + core.info(` Updated version to ${newVersion} in ${entry.name}`); + modified = true; + } + + // Update spec.dynamicArtifact tag for oci://ghcr.io references + const artifactRegex = /^(\s+dynamicArtifact:\s*oci:\/\/ghcr\.io\/[^:]+:)[^\s!]+(![^\s]+)$/m; + const artifactMatch = content.match(artifactRegex); + if (artifactMatch) { + const newTag = `bs_${backstageVersion}__${newVersion}`; + content = content.replace(artifactRegex, `$1${newTag}$2`); + core.info(` Updated dynamicArtifact tag in ${entry.name}`); + modified = true; + } + + if (modified) { + treeEntries.push({ + path: `${workspacePath}/metadata/${entry.name}`, + mode: '100644', + content, + }); + } + } + + if (treeEntries.length > 0) { + core.info(`Updated ${treeEntries.length} metadata file(s)`); + } + + return treeEntries; + } + const workspaceCheck = await checkWorkspace(overlayRepoBranchName); if (workspaceCheck.status === 'sourceEqual') { core.info( @@ -379,6 +471,11 @@ Workspace reference should be manually set to commit ${workspaceCommit}.`, core.info(`Deleting the overridden \`backstage.json\` because it's out-of-sync (\`${workspaceCheck.backstageVersionOverride}\`) with the backstage version of the new source commit (\`${backstageVersion}\`)`); } + // Update metadata files with current plugin versions + const metadataTreeEntries = await updateMetadataFiles( + prBranchExists ? targetPRBranchName : overlayRepoBranchName + ); + /** @type { Parameters[0] } */ const createTreeOptions = { owner: overlayRepoOwner, @@ -387,6 +484,7 @@ Workspace reference should be manually set to commit ${workspaceCommit}.`, tree: [ { path: `${workspacePath}/plugins-list.yaml`, mode: '100644', content: updatedPluginsYamlContent }, { path: `${workspacePath}/source.json`, mode: '100644', content: newSourceJsonContent }, + ...metadataTreeEntries, ] }; if (deleteBackstageJson) {