Skip to content

Add workflow upsert-github-repositories #32

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Dec 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
6 changes: 6 additions & 0 deletions __tests__/cli/__snapshots__/workflows.test.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@ exports[`list - Non-Interactive Mode Should provide a list of available workflow
{
"description": "Check the organizations stored and update the information with the GitHub API.",
"name": "update-github-orgs",
"workflow": [Function],
},
{
"description": "Check the organizations stored and update/create the information related to the repositories with the GitHub API.",
"name": "upsert-github-repositories",
"workflow": [Function],
},
]
`;
87 changes: 73 additions & 14 deletions __tests__/cli/workflows.test.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
const inquirer = require('inquirer').default
const knexInit = require('knex')
const { simplifyObject } = require('@ulisesgascon/simplify-object')
const { getConfig } = require('../../src/config')
const { runWorkflowCommand, listWorkflowCommand } = require('../../src/cli')
const { resetDatabase, getAllProjects, getAllGithubOrgs, addGithubOrg, addProject } = require('../../__utils__')
const { resetDatabase, getAllProjects, getAllGithubOrgs, addGithubOrg, addProject, getAllGithubRepos, addGithubRepo } = require('../../__utils__')
const { github } = require('../../src/providers')
const { sampleGithubOrg } = require('../../__fixtures__')
const { sampleGithubOrg, sampleGithubListOrgRepos, sampleGithubRepository } = require('../../__fixtures__')

const { dbSettings } = getConfig('test')

Expand Down Expand Up @@ -47,23 +48,23 @@ describe('run GENERIC - Non-Interactive Mode', () => {
})

describe('run update-github-orgs', () => {
// Mock inquirer for testing
jest.spyOn(inquirer, 'prompt').mockImplementation(async (questions) => {
const questionMap = {
'What is the name of the workflow?': 'update-github-orgs'
}
return questions.reduce((acc, question) => {
acc[question.name] = questionMap[question.message]
return acc
}, {})
})
// // Mock inquirer for testing
// jest.spyOn(inquirer, 'prompt').mockImplementation(async (questions) => {
// const questionMap = {
// 'What is the name of the workflow?': 'update-github-orgs'
// }
// return questions.reduce((acc, question) => {
// acc[question.name] = questionMap[question.message]
// return acc
// }, {})
// })

test('Should throw an error when no Github orgs are stored in the database', async () => {
const projects = await getAllProjects(knex)
expect(projects.length).toBe(0)
const githubOrgs = await getAllGithubOrgs(knex)
expect(githubOrgs.length).toBe(0)
await expect(runWorkflowCommand(knex, {}))
await expect(runWorkflowCommand(knex, { name: 'update-github-orgs' }))
.rejects
.toThrow('No organizations found. Please add organizations/projects before running this workflow.')
})
Expand All @@ -79,7 +80,7 @@ describe('run update-github-orgs', () => {
expect(githubOrgs[0].description).toBe(null)
// Mock the fetchOrgByLogin method
jest.spyOn(github, 'fetchOrgByLogin').mockResolvedValue(sampleGithubOrg)
await runWorkflowCommand(knex, {})
await runWorkflowCommand(knex, { name: 'update-github-orgs' })
// Check the database changes
githubOrgs = await getAllGithubOrgs(knex)
expect(githubOrgs.length).toBe(1)
Expand All @@ -88,3 +89,61 @@ describe('run update-github-orgs', () => {

test.todo('Should throw an error when the Github API is not available')
})

describe('run upsert-github-repositories', () => {
test('Should throw an error when no Github orgs are stored in the database', async () => {
const projects = await getAllProjects(knex)
expect(projects.length).toBe(0)
const githubOrgs = await getAllGithubOrgs(knex)
expect(githubOrgs.length).toBe(0)
await expect(runWorkflowCommand(knex, { name: 'upsert-github-repositories' }))
.rejects
.toThrow('No organizations found. Please add organizations/projects before running this workflow.')
})
test('Should add the repositories related to the organization', async () => {
// Prepare the database
const project = await addProject(knex, { name: sampleGithubOrg.login, category: 'impact' })
await addGithubOrg(knex, { login: sampleGithubOrg.login, html_url: sampleGithubOrg.html_url, project_id: project.id })
const projects = await getAllProjects(knex)
expect(projects.length).toBe(1)
const githubOrgs = await getAllGithubOrgs(knex)
expect(githubOrgs.length).toBe(1)
let githubRepos = await getAllGithubRepos(knex)
expect(githubRepos.length).toBe(0)
// Mock the github methods used
jest.spyOn(github, 'fetchOrgReposListByLogin').mockResolvedValue(sampleGithubListOrgRepos)
jest.spyOn(github, 'fetchRepoByFullName').mockResolvedValue(sampleGithubRepository)
await runWorkflowCommand(knex, { name: 'upsert-github-repositories' })
// Check the database changes
githubRepos = await getAllGithubRepos(knex)
expect(githubRepos.length).toBe(1)
expect(githubRepos[0].description).toBe(sampleGithubRepository.description)
})
test('Should update the repositories related to the organization', async () => {
// Prepare the database
const project = await addProject(knex, { name: sampleGithubOrg.login, category: 'impact' })
const org = await addGithubOrg(knex, { login: sampleGithubOrg.login, html_url: sampleGithubOrg.html_url, project_id: project.id })
const githubRepoData = simplifyObject(sampleGithubRepository, {
include: ['node_id', 'name', 'full_name', 'html_url', 'url', 'git_url', 'ssh_url', 'clone_url', 'visibility', 'default_branch']
})
githubRepoData.github_organization_id = org.id
githubRepoData.description = 'existing data'
await addGithubRepo(knex, githubRepoData)
const projects = await getAllProjects(knex)
expect(projects.length).toBe(1)
const githubOrgs = await getAllGithubOrgs(knex)
expect(githubOrgs.length).toBe(1)
let githubRepos = await getAllGithubRepos(knex)
expect(githubRepos.length).toBe(1)
expect(githubRepos[0].description).toBe('existing data')
// Mock the github methods used
jest.spyOn(github, 'fetchOrgReposListByLogin').mockResolvedValue(sampleGithubListOrgRepos)
jest.spyOn(github, 'fetchRepoByFullName').mockResolvedValue(sampleGithubRepository)
await runWorkflowCommand(knex, { name: 'upsert-github-repositories' })
// Check the database changes
githubRepos = await getAllGithubRepos(knex)
expect(githubRepos.length).toBe(1)
expect(githubRepos[0].description).toBe(sampleGithubRepository.description)
})
test.todo('Should throw an error when the Github API is not available')
})
11 changes: 10 additions & 1 deletion __utils__/index.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
const resetDatabase = async (knex) => {
await knex.raw('TRUNCATE TABLE github_repositories RESTART IDENTITY CASCADE')
await knex.raw('TRUNCATE TABLE github_organizations RESTART IDENTITY CASCADE')
await knex.raw('TRUNCATE TABLE projects RESTART IDENTITY CASCADE')
}
Expand All @@ -16,10 +17,18 @@ const addGithubOrg = async (knex, data) => {
return githubOrg
}

const getAllGithubRepos = (knex) => knex('github_repositories').select('*')
const addGithubRepo = async (knex, data) => {
const [githubRepo] = await knex('github_repositories').insert(data).returning('*')
return githubRepo
}

module.exports = {
resetDatabase,
getAllProjects,
getAllGithubOrgs,
addProject,
addGithubOrg
addGithubOrg,
getAllGithubRepos,
addGithubRepo
}
16 changes: 11 additions & 5 deletions src/cli/workflows.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
const inquirer = require('inquirer').default
const { updateGithubOrgs } = require('../workflows')
const debug = require('debug')('cli:workflows')
const { updateGithubOrgs, upsertGithubRepositories } = require('../workflows')
const { logger } = require('../utils')

const commandList = [{
name: 'update-github-orgs',
description: 'Check the organizations stored and update the information with the GitHub API.'
description: 'Check the organizations stored and update the information with the GitHub API.',
workflow: updateGithubOrgs
}, {
name: 'upsert-github-repositories',
description: 'Check the organizations stored and update/create the information related to the repositories with the GitHub API.',
workflow: upsertGithubRepositories
}]

const validCommandNames = commandList.map(({ name }) => name)
Expand Down Expand Up @@ -32,9 +38,9 @@ async function runWorkflowCommand (knex, options = {}) {
}
])

if (answers.name === 'update-github-orgs') {
await updateGithubOrgs(knex)
}
const command = commandList.find(({ name }) => name === answers.name)
debug(`Running workflow: ${command.name}`)
await command.workflow(knex)

return answers
}
Expand Down
94 changes: 94 additions & 0 deletions src/database/migrations/1733435340266_add_github_repos.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
exports.up = async (knex) => {
// Create 'github_repositories' table
await knex.schema.createTable('github_repositories', (table) => {
table.increments('id').primary() // Primary key
table.string('node_id').unique().notNullable()
table.string('name').notNullable()
table.string('full_name').notNullable()
table.string('html_url').notNullable()
table.text('description')
table.boolean('fork')
table.string('url').notNullable()
table.string('git_url').notNullable()
table.string('ssh_url').notNullable()
table.string('clone_url').notNullable()
table.string('svn_url')
table.string('homepage')
table.integer('size')
table.integer('stargazers_count')
table.integer('watchers_count')
table.string('language')
table.boolean('has_issues')
table.boolean('has_projects')
table.boolean('has_downloads')
table.boolean('has_wiki')
table.boolean('has_pages')
table.boolean('has_discussions')
table.integer('forks_count')
table.string('mirror_url')
table.boolean('archived')
table.boolean('disabled')
table.integer('open_issues_count')
table.boolean('allow_forking')
table.boolean('is_template')
table.boolean('web_commit_signoff_required')
table.specificType('topics', 'text[]') // Array of strings
table.enu('visibility', ['public', 'private', 'internal']).notNullable()
table.string('default_branch').notNullable()
table.boolean('allow_squash_merge')
table.boolean('allow_merge_commit')
table.boolean('allow_rebase_merge')
table.boolean('allow_auto_merge')
table.boolean('delete_branch_on_merge')
table.boolean('allow_update_branch')
table.boolean('use_squash_pr_title_as_default')
table.string('squash_merge_commit_message')
table.string('squash_merge_commit_title')
table.string('merge_commit_message')
table.string('merge_commit_title')
table.integer('network_count')
table.integer('subscribers_count')
table.integer('github_repo_id').unique()
table.timestamp('github_created_at')
table.timestamp('github_updated_at')
table.timestamp('github_archived_at')
table.string('license_key')
table.string('license_name')
table.string('license_spdx_id')
table.string('license_url')
table.string('license_node_id')
table.enu('secret_scanning_status', ['enabled', 'disabled']).defaultTo('disabled')
table.enu('secret_scanning_push_protection_status', ['enabled', 'disabled']).defaultTo('disabled')
table.enu('dependabot_security_updates_status', ['enabled', 'disabled']).defaultTo('disabled')
table.enu('secret_scanning_non_provider_patterns_status', ['enabled', 'disabled']).defaultTo('disabled')
table.enu('secret_scanning_validity_checks_status', ['enabled', 'disabled']).defaultTo('disabled')

// Foreign key to 'github_organizations' table
table
.integer('github_organization_id')
.unsigned()
.references('id')
.inTable('github_organizations')
.onDelete('CASCADE') // Deletes repository if the organization is deleted
.onUpdate('CASCADE') // Updates repository if the organization ID is updated

// Timestamps
table.timestamp('created_at').defaultTo(knex.fn.now()).notNullable()
table.timestamp('updated_at').defaultTo(knex.fn.now()).notNullable()
})

// Add trigger to 'github_repositories' table
await knex.raw(`
CREATE TRIGGER set_updated_at_github_repositories
BEFORE UPDATE ON github_repositories
FOR EACH ROW
EXECUTE FUNCTION update_updated_at_column();
`)
}

exports.down = async (knex) => {
// Drop trigger
await knex.raw('DROP TRIGGER IF EXISTS set_updated_at_github_repositories ON github_repositories;')
// Drop table
await knex.schema.dropTableIfExists('github_repositories')
}
Loading
Loading