Skip to content

Commit

Permalink
Merge branch 'main-enterprise' into yj-repo-in-many-suborgs
Browse files Browse the repository at this point in the history
  • Loading branch information
decyjphr committed Sep 17, 2024
2 parents 775b7ca + 85aae4f commit 87b8a95
Show file tree
Hide file tree
Showing 13 changed files with 1,991 additions and 659 deletions.
25 changes: 25 additions & 0 deletions .github/workflows/delete-old-releases.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
name: Delete old releases
permissions: write-all

on:
workflow_dispatch:
inputs:
beforeDate:
type: string
required: true
description: YYYY-MM-DD - All releases before this date are deleted.
default: "2024-01-01"

jobs:
delete-releases:
runs-on: ubuntu-latest
steps:
- name: Delete releases
run: |
for i in $(gh release list --repo https://github.com/$GITHUB_REPOSITORY --json createdAt,tagName --limit 1000 | jq --arg date $BEFORE_DATE '.[] | select(.createdAt < $date ) | .tagName' | tr -d '"'); do gh release delete $i -y --cleanup-tag --repo https://github.com/$GITHUB_REPOSITORY ; done
echo Deleted releases before $BEFORE_DATE in https://github.com/$GITHUB_REPOSITORY >> $GITHUB_STEP_SUMMARY
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
BEFORE_DATE: ${{ inputs.beforeDate }}


5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -266,9 +266,9 @@ And the `checkrun` page will look like this:
<img width="860" alt="image" src="https://github.com/github/safe-settings/assets/57544838/893ff4e6-904c-4a07-924a-7c23dc068983">
</p>

### The Settings File
### The Settings Files

The settings file can be used to set the policies at the `org`, `suborg` or `repo` level.
The settings files can be used to set the policies at the `org`, `suborg` or `repo` level.

The following can be configured:

Expand All @@ -284,6 +284,7 @@ The following can be configured:
- `Autolinks`
- `Repository name validation` using regex pattern
- `Rulesets`
- `Environments` - wait timer, required reviewers, prevent self review, protected branches deployment branch policy, custom deployment branch policy, variables, deployment protection rules

It is possible to provide an `include` or `exclude` settings to restrict the `collaborators`, `teams`, `labels` to a list of repos or exclude a set of repos for a collaborator.

Expand Down
12 changes: 12 additions & 0 deletions app.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@ default_permissions:
repository_custom_properties: write
organization_custom_properties: admin

# Workflows, workflow runs and artifacts. (needed to read environments when repo is private or internal)
# https://developer.github.com/v3/apps/permissions/#repository-permissions-for-actions
actions: read

# Repository creation, deletion, settings, teams, and collaborators.
# https://developer.github.com/v3/apps/permissions/#permission-on-administration
administration: write
Expand All @@ -50,6 +54,10 @@ default_permissions:
# https://developer.github.com/v3/apps/permissions/#permission-on-deployments
# deployments: read

# Manage repository environments.
# https://developer.github.com/v3/apps/permissions/#repository-permissions-for-environments
environments: write

# Issues and related comments, assignees, labels, and milestones.
# https://developer.github.com/v3/apps/permissions/#permission-on-issues
issues: write
Expand Down Expand Up @@ -106,6 +114,10 @@ default_permissions:
# https://developer.github.com/v3/apps/permissions/
organization_administration: write

# Manage Actions repository variables.
# https://developer.github.com/v3/apps/permissions/#repository-permissions-for-variables
variables: write


# The name of the GitHub App. Defaults to the name specified in package.json
name: Safe Settings
Expand Down
3 changes: 3 additions & 0 deletions docs/deploy.md
Original file line number Diff line number Diff line change
Expand Up @@ -255,14 +255,17 @@ Every deployment will need an [App](https://developer.github.com/apps/).

#### Repository Permissions

- Actions: **Read-only**
- Administration: **Read & Write**
- Checks: **Read & Write**
- Commit statuses: **Read & Write**
- Contents: **Read & Write**
- Custom properties: **Read & Write**
- Environments: **Read & Write**
- Issues: **Read & Write**
- Metadata: **Read-only**
- Pull requests: **Read & Write**
- Variables: **Read & Write**

#### Organization Permissions

Expand Down
20 changes: 18 additions & 2 deletions lib/glob.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,24 @@
class Glob {
constructor (glob) {
this.glob = glob
const regexptex = glob.replace(/\//g, '\\/').replace(/\?/g, '([^\\/])').replace(/\./g, '\\.').replace(/\*/g, '([^\\/]*)')
this.regexp = new RegExp(`^${regexptex}$`, 'u')

// If not a glob pattern then just match the string.
if (!this.glob.includes('*')) {
this.regexp = new RegExp(`.*${this.glob}.*`, 'u')
return
}
this.regexptText = this.globize(this.glob)
this.regexp = new RegExp(`^${this.regexptText}$`, 'u')
}

globize (glob) {
return glob
.replace(/\\/g, '\\\\') // escape backslashes
.replace(/\//g, '\\/') // escape forward slashes
.replace(/\./g, '\\.') // escape periods
.replace(/\?/g, '([^\\/])') // match any single character except /
.replace(/\*\*/g, '.+') // match any character except /, including /
.replace(/\*/g, '([^\\/]*)') // match any character except /
}

toString () {
Expand Down
39 changes: 26 additions & 13 deletions lib/mergeDeep.js
Original file line number Diff line number Diff line change
Expand Up @@ -211,18 +211,19 @@ class MergeDeep {
}
}
const combined = []
for (const fields of Object.keys(visited)) {
combined.push(visited[fields])
}
// Elements that are not in target are additions
additions[key] = combined.filter(item => {
if (this.isObjectNotArray(item)) {
return !target.some(targetItem => GET_NAME_USERNAME_PROPERTY(item) === GET_NAME_USERNAME_PROPERTY(targetItem))
} else {
return !target.includes(item)
if (Object.keys(visited).length !== 0) {
for (const fields of Object.keys(visited)) {
combined.push(visited[fields])
}
})

// Elements that are not in target are additions
additions[key] = combined.filter(item => {
if (this.isObjectNotArray(item)) {
return !target.some(targetItem => GET_NAME_USERNAME_PROPERTY(item) === GET_NAME_USERNAME_PROPERTY(targetItem))
} else {
return !target.includes(item)
}
})
}
// Elements that not in source are deletions
if (combined.length > 0) {
// Elements that not in source are deletions
Expand All @@ -247,8 +248,21 @@ class MergeDeep {
this.compareDeep(a, visited[id], additions[additions.length - 1], modifications[modifications.length - 1], deletions[deletions.length - 1])
}
// Any addtions for the matching key must be moved to modifications
const lastAddition = additions[additions.length - 1]
const lastModification = modifications[modifications.length - 1]

if (!this.isEmpty(additions)) {
modifications = modifications.concat(additions)
for (const key in lastAddition) {
if (!lastModification[key]) {
lastModification[key] = Array.isArray(lastAddition[key]) ? [] : {}
}
if (!Array.isArray(lastAddition[key])) {
Object.assign(lastModification[key], lastAddition[key])
} else {
lastModification[key].push(...lastAddition[key])
}
}
additions.length = 0
}
// Add name attribute to the modifications to make it look better ; it won't be added otherwise as it would be the same
if (!this.isEmpty(modifications[modifications.length - 1])) {
Expand Down Expand Up @@ -364,5 +378,4 @@ class MergeDeep {
}
}
MergeDeep.NAME_FIELDS = NAME_FIELDS
MergeDeep.NAME_FIELDS = NAME_FIELDS
module.exports = MergeDeep
3 changes: 2 additions & 1 deletion lib/plugins/diffable.js
Original file line number Diff line number Diff line change
Expand Up @@ -93,14 +93,15 @@ module.exports = class Diffable extends ErrorStash {
}
}

// Filter out all empty entries (usually from repo override)
// Remove any null or undefined values from the diffables (usually comes from repo override)
for (const entry of filteredEntries) {
for (const key of Object.keys(entry)) {
if (entry[key] === null || entry[key] === undefined) {
delete entry[key]
}
}
}
// Delete any diffable that now only has name and no other attributes
filteredEntries = filteredEntries.filter(entry => Object.keys(entry).filter(key => !MergeDeep.NAME_FIELDS.includes(key)).length !== 0)

const changes = []
Expand Down
132 changes: 110 additions & 22 deletions lib/plugins/environments.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
const Diffable = require('./diffable')
const MergeDeep = require('../mergeDeep')
const NopCommand = require('../nopcommand')

module.exports = class Environments extends Diffable {
constructor(...args) {
Expand All @@ -14,7 +16,7 @@ module.exports = class Environments extends Diffable {
});
}
})
}
}
}

async find() {
Expand Down Expand Up @@ -78,7 +80,7 @@ module.exports = class Environments extends Diffable {
const wait_timer = existing.wait_timer !== attrs.wait_timer;
const prevent_self_review = existing.prevent_self_review !== attrs.prevent_self_review;
const reviewers = JSON.stringify(existing.reviewers.sort((x1, x2) => x1.id - x2.id)) !== JSON.stringify(attrs.reviewers.sort((x1, x2) => x1.id - x2.id));

let existing_custom_branch_policies = existing.deployment_branch_policy === null ? null : existing.deployment_branch_policy.custom_branch_policies;
if(typeof(existing_custom_branch_policies) === 'object' && existing_custom_branch_policies !== null) {
existing_custom_branch_policies = existing_custom_branch_policies.sort();
Expand Down Expand Up @@ -158,6 +160,7 @@ module.exports = class Environments extends Diffable {

if(variables) {
let existingVariables = [...existing.variables];

for(let variable of attrs.variables) {
const existingVariable = existingVariables.find((_var) => _var.name === variable.name);
if(existingVariable) {
Expand Down Expand Up @@ -195,6 +198,7 @@ module.exports = class Environments extends Diffable {

if(deployment_protection_rules) {
let existingRules = [...existing.deployment_protection_rules];

for(let rule of attrs.deployment_protection_rules) {
const existingRule = existingRules.find((_rule) => _rule.id === rule.id);

Expand Down Expand Up @@ -227,13 +231,14 @@ module.exports = class Environments extends Diffable {
wait_timer: attrs.wait_timer,
prevent_self_review: attrs.prevent_self_review,
reviewers: attrs.reviewers,
deployment_branch_policy: attrs.deployment_branch_policy === null ? null : {
protected_branches: attrs.deployment_branch_policy.protected_branches,
deployment_branch_policy: attrs.deployment_branch_policy == null ? null : {
protected_branches: !!attrs.deployment_branch_policy.protected_branches,
custom_branch_policies: !!attrs.deployment_branch_policy.custom_branch_policies
}
});

if(attrs.deployment_branch_policy && attrs.deployment_branch_policy.custom_branch_policies) {

for(let policy of attrs.deployment_branch_policy.custom_branch_policies) {
await this.github.request('POST /repos/:org/:repo/environments/:environment_name/deployment-branch-policies', {
org: this.repo.owner,
Expand All @@ -242,26 +247,34 @@ module.exports = class Environments extends Diffable {
name: policy.name
});
}
}


for(let variable of attrs.variables) {
await this.github.request(`POST /repos/:org/:repo/environments/:environment_name/variables`, {
org: this.repo.owner,
repo: this.repo.repo,
environment_name: attrs.name,
name: variable.name,
value: variable.value
});
}

for(let rule of attrs.deployment_protection_rules) {
await this.github.request(`POST /repos/:org/:repo/environments/:environment_name/deployment_protection_rules`, {
org: this.repo.owner,
repo: this.repo.repo,
environment_name: attrs.name,
integration_id: rule.app_id
});
if(attrs.variables) {

for(let variable of attrs.variables) {
await this.github.request(`POST /repos/:org/:repo/environments/:environment_name/variables`, {
org: this.repo.owner,
repo: this.repo.repo,
environment_name: attrs.name,
name: variable.name,
value: variable.value
});
}

}

if(attrs.deployment_protection_rules) {

for(let rule of attrs.deployment_protection_rules) {
await this.github.request(`POST /repos/:org/:repo/environments/:environment_name/deployment_protection_rules`, {
org: this.repo.owner,
repo: this.repo.repo,
environment_name: attrs.name,
integration_id: rule.app_id
});
}

}
}

Expand All @@ -272,4 +285,79 @@ module.exports = class Environments extends Diffable {
environment_name: existing.name
});
}
}

sync () {
const resArray = []
if (this.entries) {
let filteredEntries = this.filterEntries()
return this.find().then(existingRecords => {

// Remove any null or undefined values from the diffables (usually comes from repo override)
for (const entry of filteredEntries) {
for (const key of Object.keys(entry)) {
if (entry[key] === null || entry[key] === undefined) {
delete entry[key]
}
}
}
// For environments, we want to keep the entries with only name defined.

const changes = []

existingRecords.forEach(x => {
if (!filteredEntries.find(y => this.comparator(x, y))) {
const change = this.remove(x).then(res => {
if (this.nop) {
return resArray.push(res)
}
return res
})
changes.push(change)
}
})

filteredEntries.forEach(attrs => {
const existing = existingRecords.find(record => {
return this.comparator(record, attrs)
})

if (!existing) {
const change = this.add(attrs).then(res => {
if (this.nop) {
return resArray.push(res)
}
return res
})
changes.push(change)
} else if (this.changed(existing, attrs)) {
const change = this.update(existing, attrs).then(res => {
if (this.nop) {
return resArray.push(res)
}
return res
})
changes.push(change)
}
})

if (this.nop) {
return Promise.resolve(resArray)
}
return Promise.all(changes)
}).catch(e => {
if (this.nop) {
if (e.status === 404) {
// Ignore 404s which can happen in dry-run as the repo may not exist.
return Promise.resolve(resArray)
} else {
resArray.push(new NopCommand(this.constructor.name, this.repo, null, `error ${e} in ${this.constructor.name} for repo: ${JSON.stringify(this.repo)} entries ${JSON.stringify(this.entries)}`, 'ERROR'))
return Promise.resolve(resArray)
}
} else {
this.logError(`Error ${e} in ${this.constructor.name} for repo: ${JSON.stringify(this.repo)} entries ${JSON.stringify(this.entries)}`)
}
})
}
}

}
Loading

0 comments on commit 87b8a95

Please sign in to comment.