From d5c346657c6b9f920399f446e4d81dc9ef795708 Mon Sep 17 00:00:00 2001 From: Alex Corvin Date: Mon, 26 Aug 2024 13:29:46 -0400 Subject: [PATCH] Restore organization membership automation (#229) * Restore organization membership automation We have decided to resume using this tool to manage organization membership. This change adds the organization config back along with a new set of automation to manage it. * Add CODEOWNERS file and update PR template --- .github/CODEOWNERS | 12 + .../ISSUE_TEMPLATE/membership_request.yaml | 59 ----- .github/PULL_REQUEST_TEMPLATE.md | 4 + .github/workflows/apply-org-membership.yaml | 65 ++++++ .github/workflows/verify-pull-requests.yaml | 109 +++++++++ README.md | 56 ++++- config/organization_admins.yaml | 8 + config/organization_members.yaml | 207 ++++++++++++++++++ 8 files changed, 449 insertions(+), 71 deletions(-) create mode 100644 .github/CODEOWNERS delete mode 100644 .github/ISSUE_TEMPLATE/membership_request.yaml create mode 100644 .github/PULL_REQUEST_TEMPLATE.md create mode 100644 .github/workflows/apply-org-membership.yaml create mode 100644 .github/workflows/verify-pull-requests.yaml create mode 100644 config/organization_admins.yaml create mode 100644 config/organization_members.yaml diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 00000000..954161b2 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,12 @@ +# Assigning ownership for specific files or directories +# Syntax: + +# Assigning ownership for all files in a directory +# Syntax: /* + +# Assigning ownership for all files in a repository +# Syntax: * + +* @opendatahub-io/Administrators + +/config/organization_members.yaml @opendatahub-io/Administrators @opendatahub-io/org-membership-maintainers \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/membership_request.yaml b/.github/ISSUE_TEMPLATE/membership_request.yaml deleted file mode 100644 index ec80eb3d..00000000 --- a/.github/ISSUE_TEMPLATE/membership_request.yaml +++ /dev/null @@ -1,59 +0,0 @@ -name: Organization Membership Request -description: Request membership in Open Data Hub Org -labels: [ "area/github-membership" ] -title: "REQUEST: New membership for " -body: -- id: github - type: input - attributes: - label: GitHub Username - placeholder: e.g. @example_user - validations: - required: true -- id: requirements - type: checkboxes - attributes: - label: Requirements - options: - - label: I have reviewed the [community membership guidelines](community-membership.md) - required: true - - label: I have [enabled 2FA on my GitHub account](https://github.com/settings/security) - required: true - - label: I have joined to the [Open Data Hub Slack Workspace](https://odh-io.slack.com/) [Invite Link](https://join.slack.com/t/odh-io/shared_invite/zt-13hp18gxj-Yb34PfQyP9GDmKMU7AkVYw) - required: true - - label: I have subscribed to the [Open Data Hub mailing list](mailto:odh-community@googlegroups.com) - required: true - - label: I am actively contributing to 1 or more Open Data Hub subprojects - required: true - - label: I have two sponsors that meet the sponsor requirements listed in the community membership guidelines - required: true - - label: I have spoken to my sponsors ahead of this application, and they have agreed to sponsor my application - required: true - - label: I have verified that my sponsors are a reviewer or an approver in at least one OWNERS (or CODEOWNERS) file within one of the Open Data Hub GitHub organizations or a member of the steering committee - required: true -- id: sponsor_1 - type: input - attributes: - label: "Sponsor 1" - description: GitHub handle of your sponsor - placeholder: e.g. @sponsor-1 - validations: - required: true -- id: sponsor_2 - type: input - attributes: - label: "Sponsor 2" - description: GitHub handle of your sponsor - placeholder: e.g. @sponsor-2 - validations: - required: true -- id: contributions - type: textarea - attributes: - label: List of contributions to the Open Data Hub project - placeholder: | - - PRs reviewed / authored - - Issues responded to - - SIG projects I am involved with - validations: - required: true diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 00000000..f072fb68 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,4 @@ + + +## Description + diff --git a/.github/workflows/apply-org-membership.yaml b/.github/workflows/apply-org-membership.yaml new file mode 100644 index 00000000..62f0b9ae --- /dev/null +++ b/.github/workflows/apply-org-membership.yaml @@ -0,0 +1,65 @@ +--- +name: Apply Organization Membership +on: + workflow_dispatch: {} + push: + branches: + - 'main' + paths: + - "config/organization_membership.yaml" +jobs: + merge_membership_config: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@master + - uses: actions/setup-python@v5 + with: + python-version: '3.x' + - name: Install python packages + run: pip install pyyaml + - name: Merge admins and members file + uses: jannekem/run-python-script-action@v1 + with: + script: | + import yaml + + with open('config/organization_admins.yaml') as f: + admins_data = yaml.safe_load(f) + with open('config/organization_members.yaml') as f: + members_data = yaml.safe_load(f) + + merged_data = members_data.copy() + orgs = members_data['orgs'].keys() + for org in orgs: + org_admins = admins_data['orgs'].get(org, {}).get('admins', []) + merged_data['orgs'][org]['admins'] = org_admins + + merged_data = admins_data | members_data + with open('config/organization_membership.yaml', 'w') as f: + yaml.dump(merged_data, f) + - name: Save the merged membership file + uses: actions/upload-artifact@master + with: + name: membership-config + path: config/ + peribolos: + runs-on: ubuntu-latest + needs: merge_membership_config + container: + image: gcr.io/k8s-prow/peribolos + steps: + - name: Fetch the merged membership file + uses: actions/download-artifact@master + with: + name: membership-config + path: config/ + - name: Apply organization membership + run: | + echo ${{ secrets.ORG_MANAGEMENT_TOKEN }} > /.github_token.txt + echo "Applying membership config:" + cat config/organization_membership.yaml + peribolos --github-token-path /.github_token.txt \ + --config-path "config/organization_membership.yaml" \ + --fix-org --fix-org-members --github-hourly-tokens="0" \ + --min-admins=2 \ + --confirm diff --git a/.github/workflows/verify-pull-requests.yaml b/.github/workflows/verify-pull-requests.yaml new file mode 100644 index 00000000..d5c4c6ad --- /dev/null +++ b/.github/workflows/verify-pull-requests.yaml @@ -0,0 +1,109 @@ +--- +name: Verify Pull Requests +on: + - pull_request +jobs: + yamllint: + name: Yamllint + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@master + - name: Yamllint + uses: karancode/yamllint-github-action@master + with: + yamllint_comment: true + env: + GITHUB_ACCESS_TOKEN: ${{ secrets.GITHUB_TOKEN }} + check-membership-config: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@master + - uses: actions/setup-python@v5 + with: + python-version: '3.x' + - name: Install python packages + run: pip install pyyaml + - name: Check user order + uses: jannekem/run-python-script-action@v1 + with: + script: | + import yaml + + def is_list_sorted(data): + return all(data[i].lower() <= data[i + 1].lower() \ + for i in range(len(data) - 1)) + + with open('config/organization_members.yaml') as f: + data = yaml.safe_load(f) + orgs = data['orgs'].keys() + for org in orgs: + org_owners = data['orgs'][org].get('admins', []) + org_members = data['orgs'][org].get('members', []) + + if not is_list_sorted(org_owners): + print((f'The list of owners for org {org} ' + 'is not in alphabetical order!')) + exit(1) + elif not is_list_sorted(org_members): + print((f'The list of members for org {org} ' + 'is not in alphabetical order!')) + exit(1) + - name: Check for individuals in both users and admins lists + uses: jannekem/run-python-script-action@v1 + env: + ORG_OWNERS: ${{ secrets.ORG_OWNERS }} + with: + script: | + import os + import yaml + + with open('config/organization_members.yaml') as f: + members_data = yaml.safe_load(f) + with open('config/organization_admins.yaml') as f: + owners_data = yaml.safe_load(f) + orgs = members_data['orgs'].keys() + for org in orgs: + org_owners = owners_data['orgs'][org].get('admins', []) + org_members = members_data['orgs'][org].get('members', []) + + if len(set(org_owners).intersection(org_members)) > 0: + print(('There is a user listed in members that is ' + f'also listed in owners for org {org}')) + exit(1) + - name: Check for duplicate individuals + uses: jannekem/run-python-script-action@v1 + with: + script: | + import yaml + + with open('config/organization_members.yaml') as f: + data = yaml.safe_load(f) + orgs = data['orgs'].keys() + for org in orgs: + org_owners = data['orgs'][org].get('admins', []) + org_members = data['orgs'][org].get('members', []) + + if len(set(org_owners)) < len(org_owners): + print(('There is a duplicate user in the list of ' + f'owners for org {org}')) + exit(1) + if len(set(org_members)) < len(org_members): + print(('There is a duplicate user in the list of ' + f'members for org {org}')) + exit(1) + - name: Ensure that admins is not set + uses: jannekem/run-python-script-action@v1 + with: + script: | + import yaml + + with open('config/organization_members.yaml') as f: + data = yaml.safe_load(f) + orgs = data['orgs'].keys() + for org in orgs: + if 'admins' in data['orgs'][org].keys(): + print(('Changes to the org membershp that change the list ' + 'of owners are not allowed.')) + exit(1) diff --git a/README.md b/README.md index 530be062..a9c21b1c 100644 --- a/README.md +++ b/README.md @@ -1,18 +1,50 @@ -# opendatahub-io GitHub organization +# org-management -This repository is no longer used to manage organization membership. Reach out to another -member of the OpenDataHub organization if you need to be added. If you do not know any -members of the OpenDataHub organization, membership in the opendatahub-io organization can be made by opening an [issue](https://github.com/opendatahub-io/org-management/issues/new/choose). +Configuration of organization membership and automation to apply this +membership via GitHub Actions automation. -Membership in the opendatahub-io project is governed by our -[community guidelines](https://github.com/opendatahub-io/opendatahub-community/blob/main/community-membership.md). +## Making Changes to Organization Membership -## Community, discussion, contribution, and support +Addition or removal of users to/from the organization should be made by editing +the [membership config][membership_config]. -Learn how to engage with the opendatahub-io community on the -[community page](http://github.com/opendatahub-io/opendatahub-community/). +To add an individual to the organization, open a pull request adding the +individual's GitHub username to the membership config file. *Note* Please +add individuals in alphabetical order. -### Code of conduct +## Automation Setup -Participation in the Open Data Hub community is governed by the -[Open Data Hub Code of Conduct](https://github.com/opendatahub-io/opendatahub-community/blob/main/CODE_OF_CONDUCT.md). +We use [Peribolos](https://docs.prow.k8s.io/docs/components/cli-tools/peribolos/) to manage +organization membership. The membership is defined in [config/organization_members.yaml][membership_config]. +We then use a GitHub action, defined in [.github/workflows/apply-org-membership.yaml](.github/workflows/apply-org-membership.yaml) +to automatically apply the membership after any change to the membership config. + +This automation depends on a GitHub personal access token with read and write +permissions to organization members. The token value is saved in this repository as a +repository secret with name `ORG_MANAGEMENT_TOKEN`. + +## CI/CD + +We use GitHub actions to automate the functionality of this repository. To validate +new changes to this repository, we have a workflow called [Verify Pull Requests](.github/workflows/verify-pull-requests.yaml). +For every pull request to this repository, this will: + + * Lint all YAML files + * Check that the list of organization users is alphabetical + * Ensure that there are no duplicate individuals + * Ensure that changes to organization owners are not being made + +When changes to the membership config file are merged to main, an additional workflow +called [Apply Organization Membership](.github/workflows/apply-org-membership.yaml) is run. This +workflow runs the Peribolos tool and reconciles the state of the org membership to +the contencts of the config file. + +## Managing Organization Owners + +We want to prevent changes to the org management file that result in the state of organization owners +changing. The Peribolos tool currently requires that you specify owners when running it. To get around this +limitation we've split the organizational config into an [admin config](config/organization_admins.yaml) and +a [members config][membership_config]. These two configs are merged at apply time +and the merged config file is passed to Peribolos. + +[membership_config]: config/organization_members.yaml \ No newline at end of file diff --git a/config/organization_admins.yaml b/config/organization_admins.yaml new file mode 100644 index 00000000..3495053b --- /dev/null +++ b/config/organization_admins.yaml @@ -0,0 +1,8 @@ +--- +orgs: + opendatahub-io: + admins: + - accorvin + - dchourasia + - jkoehler-redhat + - riprasad diff --git a/config/organization_members.yaml b/config/organization_members.yaml new file mode 100644 index 00000000..b6d1b833 --- /dev/null +++ b/config/organization_members.yaml @@ -0,0 +1,207 @@ +--- +orgs: + opendatahub-io: + members: + - 4n4nd + - abhijeet-dhumal + - adelton + - adnankhan666 + - adrielparedes + - aduquett + - AjayJagan + - ajaypratap003 + - akchinSTC + - Al-Pragliola + - alanbraz + - alexcreasy + - alexxfan + - amsharma3 + - andrewballantyne + - anishasthana + - arslan2k12 + - asanzgom + - ashley-o0o + - aslakknutsen + - astefanutti + - atheo89 + - bartoszmajsak + - biswassri + - Bobbins228 + - bredamc + - BSynRedhat + - cam-garrison + - caponetto + - catrobson + - cfchase + - CFSNM + - cfullam1 + - christianvogt + - ChristianZaccaria + - chtyler + - ChughShilpa + - codeflare-machine-account + - dandawg + - danielezonca + - daniellutz + - DaoDaoNoCode + - devguyio + - dfeddema + - dgutride + - DharmitD + - dhirajsb + - dibryant + - diegolovison + - dimakis + - dlabaj + - dpanshug + - dsp-developers + - dtrifiro + - durandom + - ederign + - emilys314 + - erwangranger + - etirelli + - eturner24 + - evaline-ju + - ezidav + - fcami + - FedeAlonso + - fialhocoelho + - Fiona-Waters + - flaviabeo + - franciscojavierarceo + - gchickma + - gitdallas + - Gkrumbach07 + - gmarkley-VI + - gmfrasca + - goern + - grainnejenningsRH + - grdryn + - gregsheremeta + - Griffin-Sullivan + - guimou + - gzaronikas + - harshad16 + - hbelmiro + - hdefazio + - heyselbi + - HumairAK + - IKRedHat + - isinyaaa + - israel-hdez + - jackdelahunt + - jbusche + - jbyrne-redhat + - jedemo + - jeff-phillips-18 + - jenny-s51 + - jeremyeder + - jessiehuff + - jgarciao + - jiridanek + - jiripetrlik + - jlalbertson + - joerunde + - Jooho + - jpuzz0 + - jstourac + - judyobrienie + - jwforres + - kaedward + - keklundrh + - KillianGolds + - kingaj12 + - KPostOffice + - kywalker-rh + - lampajr + - lazarotti + - lburgazzoli + - ldimaggi + - lphiri + - lucferbux + - lugi0 + - m-misiura + - manaswinidas + - manosnoam + - ManuelaAnsaldo + - MarianMacik + - mattmahoneyrh + - maxamillion + - maxdebayser + - Maxusmusti + - MelissaFlinn + - mholder6 + - MichaelClifford + - milite17 + - mlassak + - mmlittle023 + - MohammadiIram + - moulalis + - mturley + - muthukumaranR + - mwaykole + - n1hility + - nehachopra27 + - NickLucche + - njhill + - oksanabaza + - openshift-ci-robot + - openshift-merge-robot + - paulovmr + - pilhuhn + - piotrpdev + - pnaik1 + - ppadti + - prashantgupta24 + - Raghul-M + - ralombar + - rareddy + - rbcgh + - RH-steve-grubb + - rimolive + - rkpattnaik780 + - rkubis + - RobGeada + - romeokienzler + - rpancham + - rsun19 + - ruivieira + - russellb + - rzbhatti + - Sara4994 + - sesheta + - shawkins + - shgriffi + - simrandhaliw + - spolti + - Srihari1192 + - StevenTobin + - sutaakar + - syaseen-rh + - taneem-ibrahim + - tarilabs + - tarukumar + - tedhtchang + - terrytangyuan + - tjohnson31415 + - tonyxrmdavidson + - uidoyen + - vaibhavjainwiz + - VaishnaviHire + - VaniHaripriya + - VanillaSpoon + - varshaprasad96 + - vconzola + - VedantMahabaleshwarkar + - winklerm + - Xaenalt + - xhagrg + - xianli123 + - yannnz + - Ygnas + - yih-wang + - ykaliuta + - YuliaKrimerman + - z103cb + - zdtsw