diff --git a/.github/workflows/labels.yml b/.github/workflows/labels.yml index 366a867..eb17c7e 100644 --- a/.github/workflows/labels.yml +++ b/.github/workflows/labels.yml @@ -9,6 +9,14 @@ on: targets: description: 'The target repositories to sync labels to (comma-separated)' required: true + add: + description: 'Whether to add labels to the target repositories' + required: false + default: true + remove: + description: 'Whether to remove labels from the target repositories' + required: false + default: false defaults: run: @@ -37,3 +45,5 @@ jobs: env: SOURCE_REPOSITORY: ${{ github.event.inputs.source }} TARGET_REPOSITORIES: ${{ github.event.inputs.targets }} + ADD_LABELS: ${{ github.event.inputs.add }} + REMOVE_LABELS: ${{ github.event.inputs.remove }} diff --git a/scripts/src/actions/shared/describe-access-changes.ts b/scripts/src/actions/shared/describe-access-changes.ts index 569ca9f..da6435e 100644 --- a/scripts/src/actions/shared/describe-access-changes.ts +++ b/scripts/src/actions/shared/describe-access-changes.ts @@ -25,57 +25,70 @@ function getAccessSummaryFrom(source: State | Config): AccessSummary { const archivedRepositories = source .getResources(Repository) .filter(repository => repository.archived) - .map(repository => repository.name) + .map(repository => repository.name.toLowerCase()) const usernames = new Set([ - ...members.map(member => member.username), - ...repositoryCollaborators.map(collaborator => collaborator.username) + ...members.map(member => member.username.toLowerCase()), + ...repositoryCollaborators.map(collaborator => + collaborator.username.toLowerCase() + ) ]) const accessSummary: AccessSummary = {} const permissions = ['admin', 'maintain', 'push', 'triage', 'pull'] for (const username of usernames) { - const role = members.find(member => member.username === username)?.role + const role = members.find( + member => member.username.toLowerCase() === username + )?.role const teams = teamMembers - .filter(teamMember => teamMember.username === username) - .map(teamMember => teamMember.team) + .filter(teamMember => teamMember.username.toLowerCase() === username) + .map(teamMember => teamMember.team.toLowerCase()) const repositoryCollaborator = repositoryCollaborators .filter( - repositoryCollaborator => repositoryCollaborator.username === username + repositoryCollaborator => + repositoryCollaborator.username.toLowerCase() === username ) .filter( repositoryCollaborator => - !archivedRepositories.includes(repositoryCollaborator.repository) + !archivedRepositories.includes( + repositoryCollaborator.repository.toLowerCase() + ) ) const teamRepository = teamRepositories - .filter(teamRepository => teams.includes(teamRepository.team)) + .filter(teamRepository => + teams.includes(teamRepository.team.toLowerCase()) + ) .filter( teamRepository => - !archivedRepositories.includes(teamRepository.repository) + !archivedRepositories.includes( + teamRepository.repository.toLowerCase() + ) ) const repositories: Record = {} for (const rc of repositoryCollaborator) { - repositories[rc.repository] = repositories[rc.repository] ?? {} + const repository = rc.repository.toLowerCase() + repositories[repository] = repositories[repository] ?? {} if ( - !repositories[rc.repository].permission || + !repositories[repository].permission || permissions.indexOf(rc.permission) < - permissions.indexOf(repositories[rc.repository].permission) + permissions.indexOf(repositories[repository].permission) ) { - repositories[rc.repository].permission = rc.permission + repositories[repository].permission = rc.permission } } for (const tr of teamRepository) { - repositories[tr.repository] = repositories[tr.repository] ?? {} + const repository = tr.repository.toLowerCase() + repositories[repository] = repositories[repository] ?? {} if ( - !repositories[tr.repository].permission || + !repositories[repository].permission || permissions.indexOf(tr.permission) < - permissions.indexOf(repositories[tr.repository].permission) + permissions.indexOf(repositories[repository].permission) ) { - repositories[tr.repository].permission = tr.permission + repositories[repository].permission = tr.permission } } @@ -134,9 +147,17 @@ export async function describeAccessChanges(): Promise { switch (change.kind) { case 'E': if (path[1] === 'role') { - lines.push( - ` - will have the role in the organization change from ${change.lhs} to ${change.rhs}` - ) + if (change.lhs === undefined) { + lines.push( + ` - will join the organization as a ${change.rhs} (remind them to accept the email invitation)` + ) + } else if (change.rhs === undefined) { + lines.push(` - will leave the organization`) + } else { + lines.push( + ` - will have the role in the organization change from ${change.lhs} to ${change.rhs}` + ) + } } else { lines.push( ` - will have the permission to ${path[2]} change from ${change.lhs} to ${change.rhs}` @@ -158,7 +179,9 @@ export async function describeAccessChanges(): Promise { } } } else { - lines.push(` - will gain ${change.rhs} permission to ${path[2]}`) + lines.push( + ` - will gain ${change.rhs.permission} permission to ${path[2]}` + ) } break case 'D': diff --git a/scripts/src/actions/sync-labels.ts b/scripts/src/actions/sync-labels.ts index 6a764ee..324ec13 100644 --- a/scripts/src/actions/sync-labels.ts +++ b/scripts/src/actions/sync-labels.ts @@ -60,6 +60,8 @@ async function sync() { const targetRepos = process.env.TARGET_REPOSITORIES?.split(',')?.map(r => r.trim() ) + const addLabels = process.env.ADD_LABELS == 'true' + const removeLabels = process.env.REMOVE_LABELS == 'true' if (!sourceRepo) { throw new Error('SOURCE_REPOSITORY environment variable not set') @@ -84,24 +86,26 @@ async function sync() { .join(', ')}` ) - // for each label in the repo, check if it exists in js-libp2p - for (const label of targetLabels) { - if (!sourceLabels.find(l => l.name === label.name)) { - core.info(`Removing ${label.name} label from ${repo} repository`) - await removeLabel(repo, label.name) + if (removeLabels) { + for (const label of targetLabels) { + if (!sourceLabels.find(l => l.name === label.name)) { + core.info(`Removing ${label.name} label from ${repo} repository`) + await removeLabel(repo, label.name) + } } } - // for each label in js-libp2p, check if it exists in the repo - for (const label of sourceLabels) { - if (!targetLabels.some(l => l.name === label.name)) { - core.info(`Adding ${label.name} label to ${repo} repository`) - await addLabel( - repo, - label.name, - label.color, - label.description || undefined - ) + if (addLabels) { + for (const label of sourceLabels) { + if (!targetLabels.some(l => l.name === label.name)) { + core.info(`Adding ${label.name} label to ${repo} repository`) + await addLabel( + repo, + label.name, + label.color, + label.description || undefined + ) + } } } } diff --git a/terraform/bootstrap/.terraform.lock.hcl b/terraform/bootstrap/.terraform.lock.hcl new file mode 100644 index 0000000..f07c39b --- /dev/null +++ b/terraform/bootstrap/.terraform.lock.hcl @@ -0,0 +1,22 @@ +# This file is maintained automatically by "terraform init". +# Manual edits may be lost in future updates. + +provider "registry.terraform.io/hashicorp/aws" { + version = "4.5.0" + constraints = "4.5.0" + hashes = [ + "h1:PR5m6lcJZzSIYqfhnMd0YWTN+On2XGgfYV5AKIvOvBo=", + "zh:0573de96ba316d808be9f8d6fc8e8e68e0e6b614ed4d707bd236c4f7b46ac8b1", + "zh:37560469042f5f43fdb961eb6e6b0a8f95057df68af2c1168d5b8c66ddcb1512", + "zh:44bb4f6bc1f58e19b8bf7041f981a2549a351762d17dd39654eb24d1fa7991c7", + "zh:53af6557b68e547ac5c02cfd0e47ef63c8e9edfacf46921ccc97d73c0cd362c9", + "zh:578a583f69a8e5947d66b2b9d6969690043b6887f6b574263be7ef05f82a82ad", + "zh:6c2d42f30db198a4e7badd7f8037ef9bd951cfd6cf40328c6a7eed96801a374e", + "zh:758f3fc4d833dbdda57a4db743cbbddc8fd8c0492df47771b848447ba7876ce5", + "zh:78241bd45e2f6102055787b3697849fee7e9c28a744ba59cad956639c1aca07b", + "zh:9b12af85486a96aedd8d7984b0ff811a4b42e3d88dad1a3fb4c0b580d04fa425", + "zh:a3a7f4699c097c7b8364d05a5df9f3bd5d005fd5736c28ec5dc8f8c0ee340512", + "zh:bf875483bf2ad6cfb4029813328cdcd9ea40f50b9f1c265f4e742fe8cc456157", + "zh:f4722596e8b5f012013f87bf4d2b7d302c248a04a144de4563b3e3f754a30c51", + ] +} diff --git a/terraform/locals.tf b/terraform/locals.tf index c996ad5..7823d22 100644 --- a/terraform/locals.tf +++ b/terraform/locals.tf @@ -4,7 +4,7 @@ locals { advanced_security = false config = yamldecode(file("${path.module}/../github/${local.organization}.yml")) state = jsondecode(file("${path.module}/${local.organization}.tfstate.json")) - resources = { + sources = { "config" = { "github_membership" = { "this" = { @@ -154,4 +154,127 @@ locals { } }.managed } + resources = { + "github_membership" = { + for item in [ + for member, config in local.sources.config.github_membership.this : { + source = "config" + index = member + } + ] : item.index => local.sources[item.source].github_membership.this[item.index] + } + "github_repository" = { + for item in [ + for repository, config in local.sources.config.github_repository.this : + try(config.archived, false) ? { + source = "state" + index = repository + } : { + source = "config" + index = repository + } + ] : item.index => local.sources[item.source].github_repository.this[item.index] + } + "github_repository_collaborator" = { + for item in flatten([ + for repository, config in local.sources.config.github_repository.this : flatten([ + try(config.archived, false) ? [ + for member, config in try(local.sources.state.github_repository_collaborator.this, {}) : { + source = "state" + index = member + } if lower(config.repository) == repository + ] : [ + for member, config in local.sources.config.github_repository_collaborator.this : { + source = "config" + index = member + } if lower(config.repository) == repository + ] + ]) + ]) : item.index => local.sources[item.source].github_repository_collaborator.this[item.index] + } + "github_branch_protection" = { + for item in flatten([ + for repository, config in local.sources.config.github_repository.this : flatten([ + try(config.archived, false) ? [ + for branch_protection, config in try(local.sources.state.github_branch_protection.this, {}) : { + source = "state" + index = branch_protection + } if split(":", branch_protection)[0] == repository + ] : [ + for branch_protection, config in local.sources.config.github_branch_protection.this : { + source = "config" + index = branch_protection + } if lower(config.repository) == repository + ] + ]) + ]) : item.index => local.sources[item.source].github_branch_protection.this[item.index] + } + "github_team" = { + for item in [ + for team, config in local.sources.config.github_team.this : { + source = "config" + index = team + } + ] : item.index => local.sources[item.source].github_team.this[item.index] + } + "github_team_repository" = { + for item in flatten([ + for repository, config in local.sources.config.github_repository.this : flatten([ + try(config.archived, false) ? [ + for team, config in try(local.sources.state.github_team_repository.this, {}) : { + source = "state" + index = team + } if lower(config.repository) == repository + ] : [ + for team, config in local.sources.config.github_team_repository.this : { + source = "config" + index = team + } if lower(config.repository) == repository + ] + ]) + ]) : item.index => local.sources[item.source].github_team_repository.this[item.index] + } + "github_team_membership" = { + for item in [ + for member, config in local.sources.config.github_team_membership.this : { + source = "config" + index = member + } + ] : item.index => local.sources[item.source].github_team_membership.this[item.index] + } + "github_repository_file" = { + for item in flatten([ + for repository, config in local.sources.config.github_repository.this : flatten([ + try(config.archived, false) ? [ + for file, config in try(local.sources.state.github_repository_file.this, {}) : { + source = "state" + index = file + } if lower(config.repository) == repository + ] : [ + for file, config in local.sources.config.github_repository_file.this : { + source = try(local.sources.state.github_repository_file.this[file].content, "") == try(config.content, "") ? "state" : "config" + index = file + } if lower(config.repository) == repository + ] + ]) + ]) : item.index => local.sources[item.source].github_repository_file.this[item.index] + } + "github_issue_label" = { + for item in flatten([ + for repository, config in local.sources.config.github_repository.this : flatten([ + try(config.archived, false) ? [ + for label, config in try(local.sources.state.github_issue_label.this, {}) : { + source = "state" + index = label + } if lower(config.repository) == repository + ] : [ + for label, config in local.sources.config.github_issue_label.this : { + source = "config" + index = label + } if lower(config.repository) == repository + ] + ]) + ]) : item.index => local.sources[item.source].github_issue_label.this[item.index] + } + } } diff --git a/terraform/resources.tf b/terraform/resources.tf index d2f4d6c..755f261 100644 --- a/terraform/resources.tf +++ b/terraform/resources.tf @@ -1,12 +1,5 @@ resource "github_membership" "this" { - for_each = { - for item in [ - for member, config in local.resources.config.github_membership.this : { - source = "config" - index = member - } - ] : item.index => local.resources[item.source].github_membership.this[item.index] - } + for_each = local.resources.github_membership username = each.value.username role = each.value.role @@ -18,18 +11,7 @@ resource "github_membership" "this" { } resource "github_repository" "this" { - for_each = { - for item in [ - for repository, config in local.resources.config.github_repository.this : - try(config.archived, false) ? { - source = "state" - index = repository - } : { - source = "config" - index = repository - } - ] : item.index => local.resources[item.source].github_repository.this[item.index] - } + for_each = local.resources.github_repository name = each.value.name allow_auto_merge = try(each.value.allow_auto_merge, null) @@ -114,23 +96,7 @@ resource "github_repository" "this" { } resource "github_repository_collaborator" "this" { - for_each = { - for item in flatten([ - for repository, config in local.resources.config.github_repository.this : flatten([ - try(config.archived, false) ? [ - for member, config in try(local.resources.state.github_repository_collaborator.this, {}) : { - source = "state" - index = member - } if lower(config.repository) == repository - ] : [ - for member, config in local.resources.config.github_repository_collaborator.this : { - source = "config" - index = member - } if lower(config.repository) == repository - ] - ]) - ]) : item.index => local.resources[item.source].github_repository_collaborator.this[item.index] - } + for_each = local.resources.github_repository_collaborator depends_on = [github_repository.this] @@ -144,27 +110,11 @@ resource "github_repository_collaborator" "this" { } resource "github_branch_protection" "this" { - for_each = { - for item in flatten([ - for repository, config in local.resources.config.github_repository.this : flatten([ - try(config.archived, false) ? [ - for branch_protection, config in try(local.resources.state.github_branch_protection.this, {}) : { - source = "state" - index = branch_protection - } if split(":", branch_protection)[0] == repository - ] : [ - for branch_protection, config in local.resources.config.github_branch_protection.this : { - source = "config" - index = branch_protection - } if lower(config.repository) == repository - ] - ]) - ]) : item.index => local.resources[item.source].github_branch_protection.this[item.index] - } + for_each = local.resources.github_branch_protection pattern = each.value.pattern - repository_id = try(each.value.repository_id, github_repository.this[lower(each.value.repository)].node_id) + repository_id = lookup(each.value, "repository_id", lookup(lookup(github_repository.this, lower(lookup(each.value, "repository", "")), {}), "node_id", null)) allows_deletions = try(each.value.allows_deletions, null) allows_force_pushes = try(each.value.allows_force_pushes, null) @@ -197,14 +147,7 @@ resource "github_branch_protection" "this" { } resource "github_team" "this" { - for_each = { - for item in [ - for team, config in local.resources.config.github_team.this : { - source = "config" - index = team - } - ] : item.index => local.resources[item.source].github_team.this[item.index] - } + for_each = local.resources.github_team name = each.value.name @@ -219,30 +162,14 @@ resource "github_team" "this" { } resource "github_team_repository" "this" { - for_each = { - for item in flatten([ - for repository, config in local.resources.config.github_repository.this : flatten([ - try(config.archived, false) ? [ - for team, config in try(local.resources.state.github_team_repository.this, {}) : { - source = "state" - index = team - } if lower(config.repository) == repository - ] : [ - for team, config in local.resources.config.github_team_repository.this : { - source = "config" - index = team - } if lower(config.repository) == repository - ] - ]) - ]) : item.index => local.resources[item.source].github_team_repository.this[item.index] - } + for_each = local.resources.github_team_repository depends_on = [github_repository.this] repository = each.value.repository permission = each.value.permission - team_id = try(each.value.team_id, github_team.this[lower(each.value.team)].id) + team_id = lookup(each.value, "team_id", lookup(lookup(github_team.this, lower(lookup(each.value, "team", "")), {}), "id", null)) lifecycle { ignore_changes = [] @@ -250,19 +177,12 @@ resource "github_team_repository" "this" { } resource "github_team_membership" "this" { - for_each = { - for item in [ - for member, config in local.resources.config.github_team_membership.this : { - source = "config" - index = member - } - ] : item.index => local.resources[item.source].github_team_membership.this[item.index] - } + for_each = local.resources.github_team_membership username = each.value.username role = each.value.role - team_id = try(each.value.team_id, github_team.this[lower(each.value.team)].id) + team_id = lookup(each.value, "team_id", lookup(lookup(github_team.this, lower(lookup(each.value, "team", "")), {}), "id", null)) lifecycle { ignore_changes = [] @@ -270,30 +190,14 @@ resource "github_team_membership" "this" { } resource "github_repository_file" "this" { - for_each = { - for item in flatten([ - for repository, config in local.resources.config.github_repository.this : flatten([ - try(config.archived, false) ? [ - for file, config in try(local.resources.state.github_repository_file.this, {}) : { - source = "state" - index = file - } if lower(config.repository) == repository - ] : [ - for file, config in local.resources.config.github_repository_file.this : { - source = try(local.resources.state.github_repository_file.this[file].content, "") == try(config.content, "") ? "state" : "config" - index = file - } if lower(config.repository) == repository - ] - ]) - ]) : item.index => local.resources[item.source].github_repository_file.this[item.index] - } + for_each = local.resources.github_repository_file repository = each.value.repository file = each.value.file content = each.value.content # Since 5.25.0 the branch attribute defaults to the default branch of the repository # branch = try(each.value.branch, null) - branch = try(each.value.branch, github_repository.this[each.value.repository].default_branch) + branch = lookup(each.value, "branch", lookup(lookup(github_repository.this, each.value.repository, {}), "default_branch", null)) overwrite_on_create = try(each.value.overwrite_on_create, true) # Keep the defaults from 4.x commit_author = try(each.value.commit_author, "GitHub") @@ -306,23 +210,7 @@ resource "github_repository_file" "this" { } resource "github_issue_label" "this" { - for_each = { - for item in flatten([ - for repository, config in local.resources.config.github_repository.this : flatten([ - try(config.archived, false) ? [ - for label, config in try(local.resources.state.github_issue_label.this, {}) : { - source = "state" - index = label - } if lower(config.repository) == repository - ] : [ - for label, config in local.resources.config.github_issue_label.this : { - source = "config" - index = label - } if lower(config.repository) == repository - ] - ]) - ]) : item.index => local.resources[item.source].github_issue_label.this[item.index] - } + for_each = local.resources.github_issue_label depends_on = [github_repository.this]