From f4799666baaea731dcb8e26c3b5ff825c79278bc Mon Sep 17 00:00:00 2001 From: Andrea Brancaleoni Date: Fri, 14 Jun 2024 15:41:40 +0200 Subject: [PATCH] New action: add-runtime-custom-property --- .../add-runtime-custom-property/action.cjs | 31 ++++++++++ .../add-runtime-custom-property/action.yml | 40 +++++++++++++ src/kubeGetRepositories.js | 46 +++++++++++++++ src/updateRuntimeProperty.js | 58 +++++++++++++++++++ 4 files changed, 175 insertions(+) create mode 100644 actions/add-runtime-custom-property/action.cjs create mode 100644 actions/add-runtime-custom-property/action.yml create mode 100644 src/kubeGetRepositories.js create mode 100644 src/updateRuntimeProperty.js diff --git a/actions/add-runtime-custom-property/action.cjs b/actions/add-runtime-custom-property/action.cjs new file mode 100644 index 00000000..f754a770 --- /dev/null +++ b/actions/add-runtime-custom-property/action.cjs @@ -0,0 +1,31 @@ +module.exports = async ({ github, context, inputs, actionPath, core, debug = false }) => { + const { default: kubeGetRepositories } = await import(`${actionPath}/src/kubeGetRepositories.js`) + const { default: updateRuntimeProperty } = await import(`${actionPath}/src/updateRuntimeProperty.js`) + const org = context.repo.owner + + if (inputs.static_repositories) { + await updateRuntimeProperty({ + github, + org, + runtime: 'static', + repositories: inputs.static_repositories, + debug + }) + } + + if (inputs.runtime_directory) { + const repositories = await kubeGetRepositories({ + directory: inputs.runtime_directories, + orgFilter: new RegExp(`^${org}$`), + debug + }) + + await updateRuntimeProperty({ + github, + org, + runtime: 'k8s', + repositories, + debug + }) + } +} diff --git a/actions/add-runtime-custom-property/action.yml b/actions/add-runtime-custom-property/action.yml new file mode 100644 index 00000000..5e86f57c --- /dev/null +++ b/actions/add-runtime-custom-property/action.yml @@ -0,0 +1,40 @@ +# action that add runtime as a custom property +# to all repositories in this organization +name: add-runtime-custom-property +description: Add Runtime as Custom Property to Repositories +inputs: + github_token: + description: 'GitHub Token' + required: true + static_repositories: + description: 'Repositories that will have a `static` runtime' + required: true + runtime_directory: + description: 'Directory where runtime files are stored' + required: true + debug: + description: 'Debug mode' + required: false +runs: + using: 'composite' + steps: + - uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2 + with: + node-version: '20.x' + - id: npm + run: cd ${{ github.action_path }}/../..; npm ci + shell: bash + - name: run + id: run + uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 + env: + DEBUG: ${{ (inputs.debug == 'true' || runner.debug) && 'true' || 'false'}} + with: + github-token: ${{ inputs.github_token }} + script: | + const actionPath = '${{ github.action_path }}/../../' + const inputs = ${{ toJson(inputs) }} + + const script = require('${{ github.action_path }}/action.cjs') + await script({github, context, inputs, actionPath, core, + debug: process.env.DEBUG === 'true'}) diff --git a/src/kubeGetRepositories.js b/src/kubeGetRepositories.js new file mode 100644 index 00000000..ecd027d7 --- /dev/null +++ b/src/kubeGetRepositories.js @@ -0,0 +1,46 @@ +export default async function kubeGetRepositories ({ + debug = false, + orgFilter, + directory +}) { + if (!directory) { + throw new Error('directory is required!') + } + + if (!orgFilter) { + orgFilter = /.*/ + } + + if (typeof orgFilter === 'string') { + orgFilter = new RegExp(orgFilter) + } + + debug = debug === 'true' || debug === true + + const fs = require('fs') + const yaml = require('js-yaml') + const glob = require('glob') + + const files = glob.sync(`${directory}/**/*.yaml`) + const repos = [] + for (const file of files) { + const content = fs.readFileSync(file, 'utf8') + yaml.loadAll(content, (doc) => { + if (doc && doc.kind === 'GitRepository' && doc.spec.url) { + repos.push(doc.spec.url) + } + }) + } + + const uniqueRepos = [...new Set(repos)].sort().map((url) => { + // url in the format of ssh://git@github.com/example-org/example-ops + // get the last two elements and return an object with organization and name + const parts = url.split('/') + const name = parts.pop() + const organization = parts.pop() + + return { organization, name } + }) + + return uniqueRepos.filter(r => orgFilter.test(r.organization)) +} diff --git a/src/updateRuntimeProperty.js b/src/updateRuntimeProperty.js new file mode 100644 index 00000000..6e9027f9 --- /dev/null +++ b/src/updateRuntimeProperty.js @@ -0,0 +1,58 @@ +export default async function updateRuntimeProperty ({ + githubToken = null, + github = null, + debug = false, + repositories, + runtime, + org +}) { + if (!github && githubToken) { + const { Octokit } = await import('octokit') + + github = new Octokit({ auth: githubToken }) + } + + if (!github && !githubToken) { + throw new Error('either githubToken or github is required!') + } + + if (!runtime) { + throw new Error('runtime is required!') + } + + if (!repositories) { + throw new Error('repositories is required!') + } + + if (!org) { + throw new Error('org is required! No token can modify more than one org property.') + } + + debug = debug === 'true' || debug === true + + // if repositories is a string, split it on spaces + if (typeof repositories === 'string') { + repositories = repositories.split(' ').map(r => r.trim()).map((r) => { + const s = r.split('/') + return { org: s[0], name: s[1] } + }).filter(r => r.org === org) + } + + for (const repo of repositories) { + if (debug) { console.log(`updating runtime property for ${repo.org}/${repo.repo}`) } + + await github.request('PATCH /orgs/{org}/properties/values', { + org: repo.org, + repository_names: [repo.name], + properties: [ + { + property_name: 'runtime', + value: runtime + } + ], + headers: { + 'X-GitHub-Api-Version': '2022-11-28' + } + }) + } +}