├── .github ├── CODEOWNERS ├── ISSUE_TEMPLATE │ ├── bug_report.md │ ├── config.yml │ ├── feature_request.md │ └── question.md ├── PULL_REQUEST_TEMPLATE.md ├── settings.yml └── workflows │ ├── docker-promote.yml │ ├── feature-branch.yml │ ├── release.yml │ └── validate-codeowners.yml ├── .gitignore ├── .travis.yml ├── Dockerfile ├── LICENSE ├── Makefile ├── README.md ├── dev.goreleaser.yaml ├── examples ├── argo-workflows-template.yaml ├── codefresh.yml ├── example.env ├── run_docker_with_env_vars.sh ├── run_docker_with_env_vars_file.sh ├── run_docker_with_local_env_vars.sh ├── run_locally_with_command_line_args.sh └── run_locally_with_env_vars.sh ├── go.mod ├── go.sum ├── images ├── codefresh-deployment-status-error.png ├── codefresh-deployment-status-pending.png ├── codefresh-deployment-status-success.png ├── codefresh-deployment-status-waiting.png ├── github-branch-protection-settings.png ├── github-branch-protection-update.png ├── github-branch-status.png ├── github-commit-status.png ├── github-status-check-error.png ├── github-status-check-pending.png └── github-status-check-success.png └── main.go /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # Use this file to define individuals or teams that are responsible for code in a repository. 2 | # Read more: 3 | # 4 | # Order is important: the last matching pattern has the highest precedence 5 | 6 | # These owners will be the default owners for everything 7 | * @cloudposse/engineering 8 | 9 | # Cloud Posse Admins must review all changes to CODEOWNERS or the mergify configuration 10 | .github/mergify.yml @cloudposse/admins 11 | .github/CODEOWNERS @cloudposse/admins 12 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: 'bug' 6 | assignees: '' 7 | 8 | --- 9 | 10 | Found a bug? Maybe our [Slack Community](https://slack.cloudposse.com) can help. 11 | 12 | [![Slack Community](https://slack.cloudposse.com/badge.svg)](https://slack.cloudposse.com) 13 | 14 | ## Describe the Bug 15 | A clear and concise description of what the bug is. 16 | 17 | ## Expected Behavior 18 | A clear and concise description of what you expected to happen. 19 | 20 | ## Steps to Reproduce 21 | Steps to reproduce the behavior: 22 | 1. Go to '...' 23 | 2. Run '....' 24 | 3. Enter '....' 25 | 4. See error 26 | 27 | ## Screenshots 28 | If applicable, add screenshots or logs to help explain your problem. 29 | 30 | ## Environment (please complete the following information): 31 | 32 | Anything that will help us triage the bug will help. Here are some ideas: 33 | - OS: [e.g. Linux, OSX, WSL, etc] 34 | - Version [e.g. 10.15] 35 | 36 | ## Additional Context 37 | Add any other context about the problem here. -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | 3 | contact_links: 4 | 5 | - name: Community Slack Team 6 | url: https://cloudposse.com/slack/ 7 | about: |- 8 | Please ask and answer questions here. 9 | 10 | - name: Office Hours 11 | url: https://cloudposse.com/office-hours/ 12 | about: |- 13 | Join us every Wednesday for FREE Office Hours (lunch & learn). 14 | 15 | - name: DevOps Accelerator Program 16 | url: https://cloudposse.com/accelerate/ 17 | about: |- 18 | Own your infrastructure in record time. We build it. You drive it. 19 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature Request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: 'feature request' 6 | assignees: '' 7 | 8 | --- 9 | 10 | Have a question? Please checkout our [Slack Community](https://slack.cloudposse.com) or visit our [Slack Archive](https://archive.sweetops.com/). 11 | 12 | [![Slack Community](https://slack.cloudposse.com/badge.svg)](https://slack.cloudposse.com) 13 | 14 | ## Describe the Feature 15 | 16 | A clear and concise description of what the bug is. 17 | 18 | ## Expected Behavior 19 | 20 | A clear and concise description of what you expected to happen. 21 | 22 | ## Use Case 23 | 24 | Is your feature request related to a problem/challenge you are trying to solve? Please provide some additional context of why this feature or capability will be valuable. 25 | 26 | ## Describe Ideal Solution 27 | 28 | A clear and concise description of what you want to happen. If you don't know, that's okay. 29 | 30 | ## Alternatives Considered 31 | 32 | Explain what alternative solutions or features you've considered. 33 | 34 | ## Additional Context 35 | 36 | Add any other context or screenshots about the feature request here. 37 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/question.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudposse/github-status-updater/d098e18e04a088e4a64e96a41db8680877210511/.github/ISSUE_TEMPLATE/question.md -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ## what 2 | * Describe high-level what changed as a result of these commits (i.e. in plain-english, what do these changes mean?) 3 | * Use bullet points to be concise and to the point. 4 | 5 | ## why 6 | * Provide the justifications for the changes (e.g. business case). 7 | * Describe why these changes were made (e.g. why do these commits fix the problem?) 8 | * Use bullet points to be concise and to the point. 9 | 10 | ## references 11 | * Link to any supporting github issues or helpful documentation to add some context (e.g. stackoverflow). 12 | * Use `closes #123`, if this PR closes a GitHub issue `#123` 13 | 14 | -------------------------------------------------------------------------------- /.github/settings.yml: -------------------------------------------------------------------------------- 1 | # These settings are synced to GitHub by https://probot.github.io/apps/settings/ 2 | _extends: .github 3 | 4 | repository: 5 | # A URL with more information about the repository 6 | homepage: https://cloudposse.com 7 | 8 | # Either `true` to enable projects for this repository, or `false` to disable them. 9 | # If projects are disabled for the organization, passing `true` will cause an API error. 10 | has_projects: false 11 | 12 | # Either `true` to enable the wiki for this repository, `false` to disable it. 13 | has_wiki: false 14 | -------------------------------------------------------------------------------- /.github/workflows/docker-promote.yml: -------------------------------------------------------------------------------- 1 | name: Docker Promote 2 | 3 | on: 4 | push: 5 | tags: 6 | - "*" 7 | 8 | permissions: 9 | contents: read 10 | packages: write 11 | 12 | concurrency: 13 | group: ${{ github.workflow }}-${{ github.ref }} 14 | cancel-in-progress: false 15 | 16 | jobs: 17 | ci-docker: 18 | runs-on: ubuntu-latest 19 | steps: 20 | - name: Set output 21 | id: vars 22 | run: echo "tag=${GITHUB_REF#refs/*/}" >> $GITHUB_OUTPUT 23 | 24 | - uses: cloudposse/github-action-docker-promote@0.3.0 25 | id: promote 26 | with: 27 | registry: ghcr.io 28 | organization: "${{ github.event.repository.owner.login }}" 29 | repository: "${{ github.event.repository.name }}" 30 | login: "${{ github.actor }}" 31 | password: "${{ secrets.GITHUB_TOKEN }}" 32 | platforms: linux/amd64,linux/arm64 33 | from: sha-${{ github.sha }} 34 | to: ${{ steps.vars.outputs.tag }} 35 | use_metadata: false 36 | -------------------------------------------------------------------------------- /.github/workflows/feature-branch.yml: -------------------------------------------------------------------------------- 1 | name: Feature Branch 2 | 3 | on: 4 | workflow_dispatch: 5 | 6 | pull_request: 7 | types: 8 | - opened 9 | - synchronize 10 | - reopened 11 | 12 | push: 13 | branches: 14 | - main 15 | - release/v* 16 | paths-ignore: 17 | - '.github/**' 18 | - 'docs/**' 19 | - 'examples/**' 20 | - 'test/**' 21 | 22 | permissions: 23 | contents: read 24 | packages: write 25 | 26 | concurrency: 27 | group: ${{ github.workflow }}-${{ github.ref }} 28 | cancel-in-progress: false 29 | 30 | jobs: 31 | ci-go: 32 | runs-on: ubuntu-latest 33 | steps: 34 | - name: Checkout 35 | uses: actions/checkout@v4 36 | with: 37 | fetch-depth: 0 38 | 39 | - name: Set up Go 40 | uses: actions/setup-go@v5 41 | with: 42 | go-version-file: go.mod 43 | 44 | - name: Test Snapshot Release 45 | uses: goreleaser/goreleaser-action@v5 46 | with: 47 | distribution: goreleaser 48 | version: latest 49 | args: release --config ./dev.goreleaser.yaml --clean --snapshot 50 | 51 | - name: Upload Test Release Assets 52 | uses: actions/upload-artifact@v4 53 | with: 54 | name: github-status-updater 55 | path: dist/* 56 | retention-days: 3 57 | 58 | ci-docker: 59 | runs-on: ubuntu-latest 60 | steps: 61 | - name: "Checkout source code at current commit" 62 | uses: actions/checkout@v4 63 | 64 | - name: Build 65 | id: build 66 | uses: cloudposse/github-action-docker-build-push@1.15.1 67 | with: 68 | registry: ghcr.io 69 | organization: "${{ github.event.repository.owner.login }}" 70 | repository: "${{ github.event.repository.name }}" 71 | login: "${{ github.actor }}" 72 | password: "${{ secrets.GITHUB_TOKEN }}" 73 | platforms: linux/amd64,linux/arm64 74 | 75 | release: 76 | if: github.event_name == 'push' 77 | needs: [ci-go, ci-docker] 78 | uses: cloudposse/.github/.github/workflows/shared-go-auto-release.yml@main 79 | with: 80 | publish: true 81 | format: binary 82 | secrets: inherit 83 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | on: 3 | release: 4 | types: [published] 5 | 6 | permissions: {} 7 | 8 | concurrency: 9 | group: ${{ github.workflow }} 10 | cancel-in-progress: false 11 | 12 | jobs: 13 | perform: 14 | uses: cloudposse/.github/.github/workflows/shared-release-branches.yml@main 15 | secrets: inherit 16 | -------------------------------------------------------------------------------- /.github/workflows/validate-codeowners.yml: -------------------------------------------------------------------------------- 1 | name: Validate Codeowners 2 | on: 3 | workflow_dispatch: 4 | 5 | pull_request: 6 | 7 | jobs: 8 | validate-codeowners: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - name: "Checkout source code at current commit" 12 | uses: actions/checkout@v4 13 | - uses: mszostok/codeowners-validator@v0.7.1 14 | if: github.event.pull_request.head.repo.full_name == github.repository 15 | name: "Full check of CODEOWNERS" 16 | with: 17 | # For now, remove "files" check to allow CODEOWNERS to specify non-existent 18 | # files so we can use the same CODEOWNERS file for Terraform and non-Terraform repos 19 | # checks: "files,syntax,owners,duppatterns" 20 | checks: "syntax,owners,duppatterns" 21 | owner_checker_allow_unowned_patterns: "false" 22 | # GitHub access token is required only if the `owners` check is enabled 23 | github_access_token: "${{ secrets.PUBLIC_REPO_ACCESS_TOKEN }}" 24 | - uses: mszostok/codeowners-validator@v0.7.1 25 | if: github.event.pull_request.head.repo.full_name != github.repository 26 | name: "Syntax check of CODEOWNERS" 27 | with: 28 | checks: "syntax,duppatterns" 29 | owner_checker_allow_unowned_patterns: "false" 30 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | *.iml 3 | dist/bin/* 4 | github-status-updater 5 | .build-harness 6 | build-harness 7 | 8 | # Binaries for programs and plugins 9 | *.exe 10 | *.dll 11 | *.so 12 | *.dylib 13 | 14 | # Test binary, build with `go test -c` 15 | *.test 16 | 17 | # Output of the go coverage tool, specifically when used with LiteIDE 18 | *.out 19 | 20 | dist/ 21 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: required 2 | language: go 3 | go: 4 | - 1.11.x 5 | 6 | addons: 7 | apt: 8 | packages: 9 | - git 10 | - make 11 | - curl 12 | 13 | env: 14 | - DOCKER_IMAGE_NAME=cloudposse/github-status-updater 15 | 16 | services: 17 | - docker 18 | 19 | install: 20 | - make init 21 | - make travis:docker-login 22 | - make go:deps-build 23 | - make go:deps-dev 24 | - make go-get 25 | 26 | script: 27 | - make go:deps 28 | - make go:test 29 | - make go:lint 30 | - make go:build-all 31 | - ls -l release/ 32 | - make docker:build 33 | 34 | after_success: 35 | - make travis:docker-tag-and-push 36 | 37 | deploy: 38 | - provider: releases 39 | api_key: "$GITHUB_API_KEY" 40 | file_glob: true 41 | file: "release/*" 42 | skip_cleanup: true 43 | on: 44 | tags: true 45 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:1.20.3-bullseye as builder 2 | ENV GO111MODULE=on 3 | ENV CGO_ENABLED=0 4 | WORKDIR /usr/src/ 5 | COPY . /usr/src 6 | RUN go build -v -o "bin/github-status-updater" *.go 7 | 8 | FROM alpine:3.17 9 | RUN apk add --no-cache ca-certificates 10 | COPY --from=builder /usr/src/bin/* /usr/bin/ 11 | ENV PATH $PATH:/usr/bin 12 | ENTRYPOINT ["github-status-updater"] 13 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright 2018-2022 Cloud Posse, LLC 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | SHELL = /bin/bash 2 | 3 | PATH:=$(PATH):$(GOPATH)/bin 4 | 5 | include $(shell curl --silent -o .build-harness "https://raw.githubusercontent.com/cloudposse/build-harness/master/templates/Makefile.build-harness"; echo .build-harness) 6 | 7 | 8 | .PHONY : go-get 9 | go-get: 10 | go get 11 | 12 | 13 | .PHONY : go-build 14 | go-build: go-get 15 | CGO_ENABLED=0 go build -v -o "./dist/bin/github-status-updater" *.go 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # github-status-updater ![docker](https://github.com/cloudposse/github-status-updater/workflows/docker/badge.svg?branch=master) ![go](https://github.com/cloudposse/github-status-updater/workflows/go/badge.svg?branch=master) 2 | 3 | 4 | Command line utility for updating GitHub commit statuses and enabling required status checks for pull requests. 5 | 6 | * https://developer.github.com/v3/repos/statuses 7 | * https://help.github.com/articles/enabling-required-status-checks 8 | 9 | Useful for CI environments to set more specific commit and build statuses, including setting the target URL 10 | (the URL of the page representing the build status, or the URL of the deployed application). 11 | 12 | 13 | ![GitHub Status Checks](images/codefresh-deployment-status-success.png) 14 | ### 15 | 16 | 17 | __NOTE__: Create a [GitHub token](https://help.github.com/articles/creating-an-access-token-for-command-line-use) with `repo:status` and `public_repo` scopes 18 | 19 | __NOTE__: The icons in the image above are the avatars of the users for which the GitHub access tokens are issued 20 | 21 | 22 | 23 | ## Usage 24 | 25 | __NOTE__: The module accepts parameters as command-line arguments or as ENV variables 26 | (or any combination of command-line arguments and ENV vars). 27 | Command-line arguments take precedence over ENV vars. 28 | 29 | 30 | | Command-line argument | ENV var | Description | 31 | |:----------------------|:--------------------|:-------------------------------------------------------------------------------| 32 | | action | GITHUB_ACTION | Action to perform: `update_state` or `update_branch_protection` | 33 | | token | GITHUB_TOKEN | Github access token | 34 | | owner | GITHUB_OWNER | Github repository owner | 35 | | repo | GITHUB_REPO | Github repository name | 36 | | ref | GITHUB_REF | Commit SHA, branch name or tag | 37 | | state | GITHUB_STATE | Commit state. Possible values are `pending`, `success`, `error` or `failure` | 38 | | context | GITHUB_CONTEXT | Status label. Could be the name of a CI environment (_e.g._ `my-ci`) | 39 | | description | GITHUB_DESCRIPTION | Short high level summary of the status | 40 | | url | GITHUB_TARGET_URL | URL of the page representing the status | 41 | 42 | 43 | 44 | ### build the Go program locally 45 | 46 | ```sh 47 | go get 48 | 49 | CGO_ENABLED=0 go build -v -o "./dist/bin/github-status-updater" *.go 50 | ``` 51 | 52 | 53 | ### run locally with ENV vars 54 | [run_locally_with_env_vars.sh](examples/run_locally_with_env_vars.sh) 55 | 56 | ```sh 57 | export GITHUB_ACTION=update_state 58 | export GITHUB_TOKEN=XXXXXXXXXXXXXXXX 59 | export GITHUB_OWNER=cloudposse 60 | export GITHUB_REPO=github-status-updater 61 | export GITHUB_REF=XXXXXXXXXXXXXXXX 62 | export GITHUB_STATE=success 63 | export GITHUB_CONTEXT="my-ci" 64 | export GITHUB_DESCRIPTION="Commit status with target URL" 65 | export GITHUB_TARGET_URL="https://my-ci.com/build/1" 66 | 67 | ./dist/bin/github-status-updater 68 | ``` 69 | 70 | 71 | After the above command is executed, the commit status will be updated to `success` with the target URL `https://my-ci.com/build/1` (the green check mark in the image below) 72 | 73 | ![GitHub Commit Status](images/github-commit-status.png) 74 | ### 75 | 76 | 77 | ### run locally with command-line arguments 78 | [run_locally_with_command_line_args.sh](examples/run_locally_with_command_line_args.sh) 79 | 80 | ```sh 81 | ./dist/bin/github-status-updater \ 82 | -action update_state \ 83 | -token XXXXXXXXXXXXXXXX \ 84 | -owner cloudposse \ 85 | -repo github-status-updater \ 86 | -ref XXXXXXXXXXXXXXX \ 87 | -state success \ 88 | -context "my-ci" \ 89 | -description "Commit status with target URL" \ 90 | -url "https://my-ci.com/build/1" 91 | ``` 92 | 93 | 94 | 95 | ### build the Docker image 96 | __NOTE__: it will download all `Go` dependencies and then build the program inside the container (see [`Dockerfile`](Dockerfile)) 97 | 98 | 99 | ```sh 100 | docker build --tag github-status-updater --no-cache=true . 101 | ``` 102 | 103 | 104 | 105 | ### run in a Docker container with ENV vars 106 | [run_docker_with_env_vars.sh](examples/run_docker_with_env_vars.sh) 107 | 108 | ```sh 109 | docker run -i --rm \ 110 | -e GITHUB_ACTION=update_state \ 111 | -e GITHUB_TOKEN=XXXXXXXXXXXXXXXX \ 112 | -e GITHUB_OWNER=cloudposse \ 113 | -e GITHUB_REPO=github-status-updater \ 114 | -e GITHUB_REF=XXXXXXXXXXXXXXXX \ 115 | -e GITHUB_STATE=success \ 116 | -e GITHUB_CONTEXT="my-ci" \ 117 | -e GITHUB_DESCRIPTION="Commit status with target URL" \ 118 | -e GITHUB_TARGET_URL="https://my-ci.com/build/1" \ 119 | github-status-updater 120 | ``` 121 | 122 | 123 | 124 | ### run in a Docker container with local ENV vars propagated into the container's environment 125 | [run_docker_with_local_env_vars.sh](examples/run_docker_with_local_env_vars.sh) 126 | 127 | ```sh 128 | export GITHUB_ACTION=update_state 129 | export GITHUB_TOKEN=XXXXXXXXXXXXXXXX 130 | export GITHUB_OWNER=cloudposse 131 | export GITHUB_REPO=github-status-updater 132 | export GITHUB_REF=XXXXXXXXXXXXXXXX 133 | export GITHUB_STATE=success 134 | export GITHUB_CONTEXT="my-ci" 135 | export GITHUB_DESCRIPTION="Commit status with target URL" 136 | export GITHUB_TARGET_URL="https://my-ci.com/build/1" 137 | 138 | docker run -i --rm \ 139 | -e GITHUB_ACTION \ 140 | -e GITHUB_TOKEN \ 141 | -e GITHUB_OWNER \ 142 | -e GITHUB_REPO \ 143 | -e GITHUB_REF \ 144 | -e GITHUB_STATE \ 145 | -e GITHUB_CONTEXT \ 146 | -e GITHUB_DESCRIPTION \ 147 | -e GITHUB_TARGET_URL \ 148 | github-status-updater 149 | ``` 150 | 151 | 152 | 153 | ### run in a Docker container with ENV vars declared in a file 154 | [run_docker_with_env_vars_file.sh](examples/run_docker_with_env_vars_file.sh) 155 | 156 | ```sh 157 | docker run -i --rm --env-file ./example.env github-status-updater 158 | ``` 159 | 160 | 161 | ### 162 | ### 163 | ## GitHub Required Status Checks 164 | 165 | 166 | The module can be used to update required status checks for Pull Requests. 167 | 168 | This is useful for CI environments to set build statuses with URLs to the build pages. 169 | 170 | First, repository administrators need to enforce the branch protection and required status checks before a branch is merged in a pull request or before commits on a local branch can be pushed to the protected remote branch. 171 | 172 | * https://help.github.com/articles/enabling-required-status-checks 173 | 174 | 175 | ![GitHub Branch Protection Settings](images/github-branch-protection-settings.png) 176 | 177 | 178 | Then, to add `my-ci` as a status check for branch `test` of the `github-status-updater` repo, execute the `update_branch_protection` action locally 179 | 180 | ```ssh 181 | ./dist/bin/github-status-updater \ 182 | -action update_branch_protection \ 183 | -token XXXXXXXXXXXXXXXXXXXXXX \ 184 | -owner cloudposse \ 185 | -repo github-status-updater \ 186 | -ref test \ 187 | -context my-ci 188 | ``` 189 | 190 | 191 | or in a Docker container 192 | 193 | ```ssh 194 | docker run -i --rm \ 195 | -e GITHUB_ACTION=update_branch_protection \ 196 | -e GITHUB_TOKEN=XXXXXXXXXXXXXXXX \ 197 | -e GITHUB_OWNER=cloudposse \ 198 | -e GITHUB_REPO=github-status-updater \ 199 | -e GITHUB_REF=test \ 200 | -e GITHUB_CONTEXT="my-ci" \ 201 | github-status-updater 202 | ``` 203 | 204 | 205 | After the command executes, status check `my-ci` will be enabled for the branch as shown in the image below 206 | 207 | ### 208 | ![GitHub Branch Protection Settings](images/github-branch-protection-update.png) 209 | ### 210 | 211 | 212 | When you create a pull request for the branch, `my-ci` gets a notification from GitHub, starts the build, and updates the build status to `pending` 213 | by executing the following command to update the status of the last commit (`ref XXXXXXXXXXXXXXX`) 214 | 215 | ```sh 216 | ./dist/bin/github-status-updater \ 217 | -action update_state \ 218 | -token XXXXXXXXXXXXXXXX \ 219 | -owner cloudposse \ 220 | -repo github-status-updater \ 221 | -ref XXXXXXXXXXXXXXX \ 222 | -state pending \ 223 | -context "my-ci" \ 224 | -description "still building..." \ 225 | -url "https://my-ci.com/build/1" 226 | ``` 227 | 228 | ### 229 | ![GitHub Status Check Pending](images/github-status-check-pending.png) 230 | ### 231 | 232 | 233 | In case of any build errors, `my-ci` updates the build status to `error` with the error message in the `description` parameter 234 | 235 | ```sh 236 | ./dist/bin/github-status-updater \ 237 | -action update_state \ 238 | -token XXXXXXXXXXXXXXXX \ 239 | -owner cloudposse \ 240 | -repo github-status-updater \ 241 | -ref XXXXXXXXXXXXXXX \ 242 | -state error \ 243 | -context "my-ci" \ 244 | -description "build error" \ 245 | -url "https://my-ci.com/build/1" 246 | ``` 247 | 248 | 249 | ### 250 | ![GitHub Status Check Error](images/github-status-check-error.png) 251 | ### 252 | 253 | 254 | When the build succeeds, `my-ci` updates the build status to `success` 255 | 256 | ```sh 257 | ./dist/bin/github-status-updater \ 258 | -action update_state \ 259 | -token XXXXXXXXXXXXXXXX \ 260 | -owner cloudposse \ 261 | -repo github-status-updater \ 262 | -ref XXXXXXXXXXXXXXX \ 263 | -state success \ 264 | -context "my-ci" \ 265 | -description "build completed" \ 266 | -url "https://my-ci.com/build/1" 267 | ``` 268 | 269 | ### 270 | ![GitHub Status Check Success](images/github-status-check-success.png) 271 | ### 272 | 273 | 274 | ### 275 | ## Integrating with [CodeFresh](https://codefresh.io) CI/CD Pipelines 276 | 277 | `github-status-updater` can be easily integrated into CI/CD pipelines, especially those that use containers for build steps. 278 | 279 | [codefresh.yml](examples/codefresh.yml) shows a complete example of a [CodeFresh](https://docs.codefresh.io/docs/introduction-to-codefresh-pipelines) pipeline which performs the following steps: 280 | 281 | * Builds a Docker image for the application 282 | * Builds a [Helm](https://github.com/kubernetes/helm) [chart](https://github.com/kubernetes/charts) 283 | * Pushes the Docker images (for commits, branches and tags) to CodeFresh repository 284 | * Executes `update_branch_protection` action on `github-status-updater` [Docker container](https://hub.docker.com/r/cloudposse/github-status-updater) to add `Staging Environment` as a required status check 285 | * Executes `update_state` action on `github-status-updater` [Docker container](https://hub.docker.com/r/cloudposse/github-status-updater) to update `Staging Environment` deployment status to `pending` 286 | * Deploys the Helm chart to a [Kubernetes](https://kubernetes.io) cluster 287 | * Executes `update_state` action on `github-status-updater` [Docker container](https://hub.docker.com/r/cloudposse/github-status-updater) to update `Staging Environment` deployment status to `success` 288 | 289 | 290 | ![GitHub Status Checks](images/codefresh-deployment-status-pending.png) 291 | ### 292 | 293 | ![GitHub Status Checks](images/codefresh-deployment-status-success.png) 294 | ### 295 | 296 | 297 | 298 | ## References 299 | * https://github.com/google/go-github 300 | * https://godoc.org/github.com/google/go-github/github 301 | * https://help.github.com/articles/enabling-required-status-checks 302 | * https://developer.github.com/v3/repos/statuses 303 | * https://developer.github.com/v3/guides/building-a-ci-server 304 | * https://docs.docker.com/develop/develop-images/dockerfile_best-practices 305 | * https://docs.docker.com/engine/reference/commandline/build 306 | * https://docs.docker.com/engine/reference/commandline/run 307 | * https://codefresh.io 308 | * https://docs.codefresh.io/docs/introduction-to-codefresh-pipelines 309 | * https://github.com/kubernetes/helm 310 | * https://github.com/kubernetes/charts 311 | 312 | 313 | 314 | ## Help 315 | 316 | **Got a question?** 317 | 318 | File a GitHub [issue](https://github.com/cloudposse/github-status-updater/issues), send us an [email](mailto:hello@cloudposse.com) or reach out to us on [Gitter](https://gitter.im/cloudposse/). 319 | 320 | 321 | ## Contributing 322 | 323 | ### Bug Reports & Feature Requests 324 | 325 | Please use the [issue tracker](https://github.com/cloudposse/github-status-updater/issues) to report any bugs or file feature requests. 326 | 327 | ### Developing 328 | 329 | If you are interested in being a contributor and want to get involved in developing `github-status-updater`, we would love to hear from you! Shoot us an [email](mailto:hello@cloudposse.com). 330 | 331 | In general, PRs are welcome. We follow the typical "fork-and-pull" Git workflow. 332 | 333 | 1. **Fork** the repo on GitHub 334 | 2. **Clone** the project to your own machine 335 | 3. **Commit** changes to your own branch 336 | 4. **Push** your work back up to your fork 337 | 5. Submit a **Pull request** so that we can review your changes 338 | 339 | **NOTE:** Be sure to merge the latest from "upstream" before making a pull request! 340 | 341 | 342 | ## License 343 | 344 | [APACHE 2.0](LICENSE) © 2018 [Cloud Posse, LLC](https://cloudposse.com) 345 | 346 | See [LICENSE](LICENSE) for full details. 347 | 348 | Licensed to the Apache Software Foundation (ASF) under one 349 | or more contributor license agreements. See the NOTICE file 350 | distributed with this work for additional information 351 | regarding copyright ownership. The ASF licenses this file 352 | to you under the Apache License, Version 2.0 (the 353 | "License"); you may not use this file except in compliance 354 | with the License. You may obtain a copy of the License at 355 | 356 | http://www.apache.org/licenses/LICENSE-2.0 357 | 358 | Unless required by applicable law or agreed to in writing, 359 | software distributed under the License is distributed on an 360 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 361 | KIND, either express or implied. See the License for the 362 | specific language governing permissions and limitations 363 | under the License. 364 | 365 | 366 | ## About 367 | 368 | `github-status-updater` is maintained and funded by [Cloud Posse, LLC][website]. 369 | 370 | ![Cloud Posse](https://cloudposse.com/logo-300x69.png) 371 | 372 | 373 | Like it? Please let us know at 374 | 375 | We love [Open Source Software](https://github.com/cloudposse/)! 376 | 377 | See [our other projects][community] 378 | or [hire us][hire] to help build your next cloud platform. 379 | 380 | [website]: https://cloudposse.com/ 381 | [community]: https://github.com/cloudposse/ 382 | [hire]: https://cloudposse.com/contact/ 383 | 384 | 385 | ### Contributors 386 | 387 | | [![Erik Osterman][erik_img]][erik_web]
[Erik Osterman][erik_web] | [![Andriy Knysh][andriy_img]][andriy_web]
[Andriy Knysh][andriy_web] | 388 | |-------------------------------------------------------|------------------------------------------------------------------| 389 | 390 | [erik_img]: http://s.gravatar.com/avatar/88c480d4f73b813904e00a5695a454cb?s=144 391 | [erik_web]: https://github.com/osterman/ 392 | [andriy_img]: https://avatars0.githubusercontent.com/u/7356997?v=4&u=ed9ce1c9151d552d985bdf5546772e14ef7ab617&s=144 393 | [andriy_web]: https://github.com/aknysh/ 394 | -------------------------------------------------------------------------------- /dev.goreleaser.yaml: -------------------------------------------------------------------------------- 1 | builds: 2 | - env: 3 | # goreleaser does not work with CGO, it could also complicate 4 | # usage by users in CI/CD systems like Terraform Cloud where 5 | # they are unable to install libraries. 6 | - CGO_ENABLED=0 7 | mod_timestamp: '{{ .CommitTimestamp }}' 8 | goos: 9 | - darwin 10 | - freebsd 11 | - windows 12 | - linux 13 | goarch: 14 | - amd64 15 | - '386' 16 | - arm 17 | - arm64 18 | ldflags: 19 | # -s Omit the symbol table and debug information 20 | # -w Omit the DWARF symbol table 21 | # -X importpath.name=value # set the value of the string variable in importpath named name to value 22 | # Someday we could implement a "version" command and set the version like this: 23 | # - '-s -w -X "github.com/cloudposse/github-status-updater/cmd.Version={{.Env.GORELEASER_CURRENT_TAG}}"' 24 | - '-s -w' 25 | 26 | archives: 27 | - format: binary 28 | name_template: '{{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}' 29 | 30 | checksum: 31 | name_template: '{{ .ProjectName }}_{{ .Version }}_SHA256SUMS' 32 | algorithm: sha256 33 | 34 | release: 35 | # If you want to manually examine the release before it is live, uncomment this line: 36 | # draft: true 37 | 38 | changelog: 39 | skip: true -------------------------------------------------------------------------------- /examples/argo-workflows-template.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: argoproj.io/v1alpha1 3 | kind: WorkflowTemplate 4 | metadata: 5 | name: github-notifier 6 | spec: 7 | entrypoint: generic-github-notifier 8 | templates: 9 | - name: generic-github-notifier 10 | inputs: 11 | parameters: 12 | - name: organization 13 | - name: repository 14 | - name: revision 15 | - name: context 16 | value: 'argo-workflows' 17 | - name: state 18 | - name: target_url 19 | - name: description 20 | container: 21 | image: cloudposse/github-status-updater:0.5.0 22 | env: 23 | - name: GITHUB_ACTION 24 | value: 'update_state' 25 | - name: GITHUB_STATE 26 | value: '{{inputs.parameters.state}}' 27 | - name: GITHUB_TARGET_URL 28 | value: '{{inputs.parameters.target_url}}' 29 | - name: GITHUB_DESCRIPTION 30 | value: '{{inputs.parameters.description}}' 31 | - name: GITHUB_CONTEXT 32 | value: '{{inputs.parameters.context}}' 33 | - name: GITHUB_TOKEN 34 | valueFrom: 35 | secretKeyRef: 36 | key: 'github-automation-token' 37 | name: 'github-automation-secret' 38 | - name: GITHUB_OWNER 39 | value: '{{inputs.parameters.organization}}' 40 | - name: GITHUB_REPO 41 | value: '{{inputs.parameters.repository}}' 42 | - name: GITHUB_REF 43 | value: '{{inputs.parameters.revision}}' 44 | - name: GITHUB_BASE_URL 45 | value: https://private-github-instance/api/v3/ 46 | - name: GITHUB_UPLOAD_URL 47 | value: https://private-github-instance/api/uploads 48 | - name: GITHUB_INSECURE 49 | value: 'false' 50 | resources: 51 | requests: 52 | cpu: 1m 53 | memory: 8Mi 54 | activeDeadlineSeconds: 300 55 | 56 | --- 57 | apiVersion: argoproj.io/v1alpha1 58 | kind: WorkflowTemplate 59 | metadata: 60 | name: build-template 61 | spec: 62 | arguments: 63 | parameters: 64 | - name: revision 65 | value: master 66 | entrypoint: main 67 | templates: 68 | - name: main 69 | dag: 70 | tasks: 71 | - name: notify-github-pending 72 | templateRef: 73 | name: github-notifier 74 | template: generic-github-notifier 75 | arguments: 76 | parameters: 77 | - name: repository 78 | value: 'my-repo' 79 | - name: revision 80 | value: '{{workflow.parameters.revision}}' 81 | - name: state 82 | value: 'pending' 83 | - name: target_url 84 | value: 'https://argo-workflows.example.com/workflows/{{workflow.namespace}}/{{workflow.name}}' 85 | - name: description 86 | value: 'pending' 87 | - name: organisation 88 | value: 'my-org' 89 | 90 | - name: build-task 91 | template: build-task 92 | 93 | - name: notify-github-result 94 | templateRef: 95 | name: github-notifier 96 | template: generic-github-notifier 97 | arguments: 98 | parameters: 99 | - name: repository 100 | value: 'my-repo' 101 | - name: revision 102 | value: '{{workflow.parameters.revision}}' 103 | - name: state 104 | value: "{{= tasks['build-task'].status == 'Succeeded' ? 'success' : 'failure'}}" 105 | - name: target_url 106 | value: 'https://argo-workflows.example.com/workflows/{{workflow.namespace}}/{{workflow.name}}' 107 | - name: description 108 | value: "{{= tasks['build-task'].status == 'Succeeded' ? 'build succeed' : 'build failed'}}" 109 | - name: organisation 110 | value: 'my-org' 111 | depends: build-task || build-task.Failed 112 | 113 | - name: build-task 114 | # implement your build task here 115 | -------------------------------------------------------------------------------- /examples/codefresh.yml: -------------------------------------------------------------------------------- 1 | version: '1.0' 2 | 3 | steps: 4 | init-variables: 5 | title: Init variables 6 | image: alpine 7 | commands: 8 | - cf_export BUILD_HARNESS_VERSION=feature-helm-chart-ops 9 | - cf_export GIT_BRANCH=${{CF_BRANCH}} 10 | 11 | build-image: 12 | title: Build image 13 | type: build 14 | description: Build catalogue 15 | image-name: cloudpossedemo/catalogue 16 | dockerfile: Dockerfile 17 | 18 | semver: 19 | title: Export semantic version 20 | image: cloudposse/build-harness:${{BUILD_HARNESS_VERSION}} 21 | working_directory: ${{build-image}} 22 | commands: 23 | - make git:show 24 | - make semver:show 25 | - make semver:export >> ${{CF_VOLUME_PATH}}/env_vars_to_export 26 | - make stages:export 27 | - make stages:export >> ${{CF_VOLUME_PATH}}/env_vars_to_export 28 | 29 | build-chart: 30 | title: Build Charts 31 | image: cloudposse/build-harness:${{BUILD_HARNESS_VERSION}} 32 | working_directory: ${{build-image}} 33 | commands: 34 | - REPO_NAME=cloudpossedemo REPO_ENDPOINT=${{REPO_ENDPOINT}} make helm:repo:add 35 | - make helm:repo:add-remote 36 | - make helm:chart:clean 37 | - make helm:chart:build-all 38 | - make helm:chart:publish 39 | 40 | push-image-commit: 41 | title: Push image with commit based semver tags 42 | type: push 43 | candidate: ${{build-image}} 44 | tags: 45 | - "${{SEMVERSION_COMMIT_SHORT}}" 46 | - "${{SEMVERSION_COMMIT}}" 47 | 48 | push-image-branch: 49 | title: Push image with branch based semver tags 50 | type: push 51 | candidate: ${{build-image}} 52 | tags: 53 | - "${{SEMVERSION_BRANCH}}" 54 | - "${{SEMVERSION_BRANCH_COMMIT_SHORT}}" 55 | - "${{SEMVERSION_BRANCH_COMMIT}}" 56 | when: 57 | condition: 58 | all: 59 | executeForBranch: "'${{SEMVERSION_BRANCH}}' != ''" 60 | 61 | push-image-tag: 62 | title: Push image with tag based semver tags 63 | type: push 64 | candidate: ${{build-image}} 65 | tag: "${{SEMVERSION_TAG}}" 66 | when: 67 | condition: 68 | all: 69 | executeForTag: "'${{SEMVERSION_TAG}}' != ''" 70 | 71 | push-image-latest: 72 | title: Push image with latest tag 73 | type: push 74 | candidate: ${{build-image}} 75 | tag: latest 76 | when: 77 | condition: 78 | all: 79 | executeForMasterBranch: "'${{CF_BRANCH}}' == 'master'" 80 | 81 | update-branch-protection: 82 | title: Add "Staging Environment" status check to the branch 83 | image: cloudposse/github-status-updater 84 | environment: 85 | - GITHUB_ACTION=update_branch_protection 86 | - GITHUB_TOKEN=${{GITHUB_TOKEN}} 87 | - GITHUB_OWNER=${{CF_REPO_OWNER}} 88 | - GITHUB_REPO=${{CF_REPO_NAME}} 89 | - GITHUB_REF=${{CF_BRANCH}} 90 | - GITHUB_CONTEXT=Staging Environment 91 | when: 92 | condition: 93 | all: 94 | executeForBranch: "'${{SEMVERSION_BRANCH}}' != ''" 95 | 96 | set-deployment-status-to-pending: 97 | title: Set "Staging Environment" deployment status to "pending" 98 | image: cloudposse/github-status-updater 99 | environment: 100 | - GITHUB_ACTION=update_state 101 | - GITHUB_TOKEN=${{GITHUB_TOKEN}} 102 | - GITHUB_OWNER=${{CF_REPO_OWNER}} 103 | - GITHUB_REPO=${{CF_REPO_NAME}} 104 | - GITHUB_REF=${{CF_REVISION}} 105 | - GITHUB_CONTEXT=Staging Environment 106 | - GITHUB_STATE=pending 107 | - GITHUB_DESCRIPTION=Deploying changes to ${{FEATURE}} namespace 108 | - GITHUB_TARGET_URL=http://master.demo.cloudposse.org 109 | 110 | deploy-helm: 111 | title: Deploy Helm chart 112 | image: cloudposse/cf-plugin-helm:0.2.0-fix-working-with-repo 113 | environment: 114 | - CHART_NAME=catalogue 115 | - RELEASE_NAME=catalogue-${{CF_BRANCH_TAG_NORMALIZED}} 116 | - KUBE_CONTEXT=cluster-4 117 | - NAMESPACE=${{NAMESPACE}} 118 | - CHART_VERSION=${{SEMVERSION_BRANCH}} 119 | - CHART_REPO_URL=${{REPO_ENDPOINT}} 120 | - WAIT=false 121 | - TIMEOUT=1200 122 | - custom_fullnameOverride=catalogue 123 | 124 | set-deployment-status-to-success: 125 | title: Set "Staging Environment" deployment status to "success" 126 | image: cloudposse/github-status-updater 127 | environment: 128 | - GITHUB_ACTION=update_state 129 | - GITHUB_TOKEN=${{GITHUB_TOKEN}} 130 | - GITHUB_OWNER=${{CF_REPO_OWNER}} 131 | - GITHUB_REPO=${{CF_REPO_NAME}} 132 | - GITHUB_REF=${{CF_REVISION}} 133 | - GITHUB_CONTEXT=Staging Environment 134 | - GITHUB_STATE=success 135 | - GITHUB_DESCRIPTION=Deployed to ${{FEATURE}} namespace 136 | - GITHUB_TARGET_URL=http://master.demo.cloudposse.org 137 | -------------------------------------------------------------------------------- /examples/example.env: -------------------------------------------------------------------------------- 1 | GITHUB_ACTION=update_state 2 | GITHUB_TOKEN=XXXXXXXXXXXXXXXX 3 | GITHUB_OWNER=cloudposse 4 | GITHUB_REPO=github-status-updater 5 | GITHUB_REF=XXXXXXXXXXXXXXXX 6 | GITHUB_STATE=success 7 | GITHUB_CONTEXT="my-ci" 8 | GITHUB_DESCRIPTION="Commit status with target URL" 9 | GITHUB_TARGET_URL="https://my-ci.com/build/1" 10 | -------------------------------------------------------------------------------- /examples/run_docker_with_env_vars.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | docker run -i --rm \ 4 | -e GITHUB_ACTION=update_state \ 5 | -e GITHUB_TOKEN=XXXXXXXXXXXXXXXX \ 6 | -e GITHUB_OWNER=cloudposse \ 7 | -e GITHUB_REPO=github-status-updater \ 8 | -e GITHUB_REF=XXXXXXXXXXXXXXXX \ 9 | -e GITHUB_STATE=success \ 10 | -e GITHUB_CONTEXT="my-ci" \ 11 | -e GITHUB_DESCRIPTION="Commit status with target URL" \ 12 | -e GITHUB_TARGET_URL="https://my-ci.com/build/1" \ 13 | github-status-updater 14 | -------------------------------------------------------------------------------- /examples/run_docker_with_env_vars_file.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | docker run -i --rm --env-file ./example.env github-status-updater 4 | -------------------------------------------------------------------------------- /examples/run_docker_with_local_env_vars.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | export GITHUB_ACTION=update_state 4 | export GITHUB_TOKEN=XXXXXXXXXXXXXXXX 5 | export GITHUB_OWNER=cloudposse 6 | export GITHUB_REPO=github-status-updater 7 | export GITHUB_REF=XXXXXXXXXXXXXXXX 8 | export GITHUB_STATE=success 9 | export GITHUB_CONTEXT="my-ci" 10 | export GITHUB_DESCRIPTION="Commit status with target URL" 11 | export GITHUB_TARGET_URL="https://my-ci.com/build/1" 12 | 13 | docker run -i --rm \ 14 | -e GITHUB_ACTION \ 15 | -e GITHUB_TOKEN \ 16 | -e GITHUB_OWNER \ 17 | -e GITHUB_REPO \ 18 | -e GITHUB_REF \ 19 | -e GITHUB_STATE \ 20 | -e GITHUB_CONTEXT \ 21 | -e GITHUB_DESCRIPTION \ 22 | -e GITHUB_TARGET_URL \ 23 | github-status-updater 24 | -------------------------------------------------------------------------------- /examples/run_locally_with_command_line_args.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ../dist/bin/github-status-updater \ 4 | -action update_state \ 5 | -token XXXXXXXXXXXXXXXX \ 6 | -owner cloudposse \ 7 | -repo github-status-updater \ 8 | -ref XXXXXXXXXXXXXXX \ 9 | -state success \ 10 | -context "my-ci" \ 11 | -description "Commit status with target URL" \ 12 | -url "https://my-ci.com/build/1" 13 | -------------------------------------------------------------------------------- /examples/run_locally_with_env_vars.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | export GITHUB_ACTION=update_state 4 | export GITHUB_TOKEN=XXXXXXXXXXXXXXXX 5 | export GITHUB_OWNER=cloudposse 6 | export GITHUB_REPO=github-status-updater 7 | export GITHUB_REF=XXXXXXXXXXXXXXXX 8 | export GITHUB_STATE=success 9 | export GITHUB_CONTEXT="my-ci" 10 | export GITHUB_DESCRIPTION="Commit status with target URL" 11 | export GITHUB_TARGET_URL="https://my-ci.com/build/1" 12 | 13 | ../dist/bin/github-status-updater 14 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/cloudposse/github-status-updater 2 | 3 | go 1.20 4 | 5 | require github.com/google/go-github/v42 v42.0.0 6 | 7 | require ( 8 | github.com/google/go-querystring v1.1.0 // indirect 9 | golang.org/x/crypto v0.17.0 // indirect 10 | ) 11 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 2 | github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= 3 | github.com/google/go-github/v42 v42.0.0 h1:YNT0FwjPrEysRkLIiKuEfSvBPCGKphW5aS5PxwaoLec= 4 | github.com/google/go-github/v42 v42.0.0/go.mod h1:jgg/jvyI0YlDOM1/ps6XYh04HNQ3vKf0CVko62/EhRg= 5 | github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= 6 | github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= 7 | golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k= 8 | golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= 9 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 10 | -------------------------------------------------------------------------------- /images/codefresh-deployment-status-error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudposse/github-status-updater/d098e18e04a088e4a64e96a41db8680877210511/images/codefresh-deployment-status-error.png -------------------------------------------------------------------------------- /images/codefresh-deployment-status-pending.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudposse/github-status-updater/d098e18e04a088e4a64e96a41db8680877210511/images/codefresh-deployment-status-pending.png -------------------------------------------------------------------------------- /images/codefresh-deployment-status-success.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudposse/github-status-updater/d098e18e04a088e4a64e96a41db8680877210511/images/codefresh-deployment-status-success.png -------------------------------------------------------------------------------- /images/codefresh-deployment-status-waiting.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudposse/github-status-updater/d098e18e04a088e4a64e96a41db8680877210511/images/codefresh-deployment-status-waiting.png -------------------------------------------------------------------------------- /images/github-branch-protection-settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudposse/github-status-updater/d098e18e04a088e4a64e96a41db8680877210511/images/github-branch-protection-settings.png -------------------------------------------------------------------------------- /images/github-branch-protection-update.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudposse/github-status-updater/d098e18e04a088e4a64e96a41db8680877210511/images/github-branch-protection-update.png -------------------------------------------------------------------------------- /images/github-branch-status.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudposse/github-status-updater/d098e18e04a088e4a64e96a41db8680877210511/images/github-branch-status.png -------------------------------------------------------------------------------- /images/github-commit-status.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudposse/github-status-updater/d098e18e04a088e4a64e96a41db8680877210511/images/github-commit-status.png -------------------------------------------------------------------------------- /images/github-status-check-error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudposse/github-status-updater/d098e18e04a088e4a64e96a41db8680877210511/images/github-status-check-error.png -------------------------------------------------------------------------------- /images/github-status-check-pending.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudposse/github-status-updater/d098e18e04a088e4a64e96a41db8680877210511/images/github-status-check-pending.png -------------------------------------------------------------------------------- /images/github-status-check-success.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudposse/github-status-updater/d098e18e04a088e4a64e96a41db8680877210511/images/github-status-check-success.png -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "crypto/tls" 6 | "flag" 7 | "fmt" 8 | "log" 9 | "net/http" 10 | "os" 11 | "strings" 12 | 13 | "github.com/google/go-github/v42/github" 14 | ) 15 | 16 | type roundTripper struct { 17 | accessToken string 18 | insecure bool 19 | } 20 | 21 | func (rt roundTripper) RoundTrip(r *http.Request) (*http.Response, error) { 22 | r.Header.Set("Authorization", fmt.Sprintf("token %s", rt.accessToken)) 23 | transport := http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: rt.insecure}} 24 | return transport.RoundTrip(r) 25 | } 26 | 27 | func isValidState(state string) bool { 28 | validStates := [4]string{"error", "failure", "pending", "success"} 29 | for _, s := range validStates { 30 | if state == s { 31 | return true 32 | } 33 | } 34 | return false 35 | } 36 | 37 | var ( 38 | action = flag.String("action", os.Getenv("GITHUB_ACTION"), "Action to perform: 'update_state' or 'update_branch_protection'") 39 | token = flag.String("token", os.Getenv("GITHUB_TOKEN"), "Github access token") 40 | owner = flag.String("owner", os.Getenv("GITHUB_OWNER"), "Github repository owner") 41 | repo = flag.String("repo", os.Getenv("GITHUB_REPO"), "Github repository name") 42 | ref = flag.String("ref", os.Getenv("GITHUB_REF"), "Commit SHA, branch name or tag") 43 | state = flag.String("state", os.Getenv("GITHUB_STATE"), "Commit state. Possible values are 'pending', 'success', 'error' or 'failure'") 44 | ctx = flag.String("context", os.Getenv("GITHUB_CONTEXT"), "Status label. Could be the name of a CI environment") 45 | description = flag.String("description", os.Getenv("GITHUB_DESCRIPTION"), "Short high level summary of the status") 46 | url = flag.String("url", os.Getenv("GITHUB_TARGET_URL"), "URL of the page representing the status") 47 | baseURL = flag.String("baseURL", os.Getenv("GITHUB_BASE_URL"), "Base URL of github enterprise") 48 | uploadURL = flag.String("uploadURL", os.Getenv("GITHUB_UPLOAD_URL"), "Upload URL of github enterprise") 49 | insecure = flag.Bool("insecure", strings.ToLower(os.Getenv("GITHUB_INSECURE")) == "true", "Ignore SSL certificate check") 50 | ) 51 | 52 | func getUserLogins(users []*github.User) []string { 53 | res := []string{} 54 | if users != nil && len(users) > 0 { 55 | for _, user := range users { 56 | if user != nil { 57 | res = append(res, user.GetLogin()) 58 | } 59 | } 60 | } 61 | 62 | return res 63 | } 64 | 65 | func getTeamSlugs(teams []*github.Team) []string { 66 | res := []string{} 67 | if teams != nil && len(teams) > 0 { 68 | for _, team := range teams { 69 | if team != nil { 70 | res = append(res, team.GetSlug()) 71 | } 72 | } 73 | } 74 | 75 | return res 76 | } 77 | 78 | func main() { 79 | flag.Parse() 80 | 81 | if *action == "" { 82 | flag.PrintDefaults() 83 | log.Fatal("-action or GITHUB_ACTION required") 84 | } 85 | if *action != "update_state" && *action != "update_branch_protection" { 86 | flag.PrintDefaults() 87 | log.Fatal("-action or GITHUB_ACTION must be 'update_state' or 'update_branch_protection'") 88 | } 89 | if *token == "" { 90 | flag.PrintDefaults() 91 | log.Fatal("-token or GITHUB_TOKEN required") 92 | } 93 | if *owner == "" { 94 | flag.PrintDefaults() 95 | log.Fatal("-owner or GITHUB_OWNER required") 96 | } 97 | if *repo == "" { 98 | flag.PrintDefaults() 99 | log.Fatal("-repo or GITHUB_REPO required") 100 | } 101 | 102 | http.DefaultClient.Transport = roundTripper{*token, *insecure} 103 | var githubClient *github.Client 104 | if *baseURL != "" || *uploadURL != "" { 105 | if *baseURL == "" { 106 | flag.PrintDefaults() 107 | log.Fatal("-baseURL or GITHUB_BASE_URL required when using -uploadURL or GITHUB_UPLOAD_URL") 108 | } 109 | if *uploadURL == "" { 110 | flag.PrintDefaults() 111 | log.Fatal("-uploadURL or GITHUB_UPLOAD_URL required when using -baseURL or GITHUB_BASE_URL") 112 | } 113 | githubClient, _ = github.NewEnterpriseClient(*baseURL, *uploadURL, http.DefaultClient) 114 | } else { 115 | githubClient = github.NewClient(http.DefaultClient) 116 | } 117 | 118 | // Update status of a commit 119 | if *action == "update_state" { 120 | if *ref == "" { 121 | flag.PrintDefaults() 122 | log.Fatal("-ref or GITHUB_REF is required and must be a commit SHA") 123 | } 124 | if *state == "" { 125 | flag.PrintDefaults() 126 | log.Fatal("-state or GITHUB_STATE required") 127 | } 128 | if !isValidState(*state) { 129 | flag.PrintDefaults() 130 | log.Fatal("-state or GITHUB_STATE must be one of 'error', 'failure', 'pending', 'success'") 131 | } 132 | 133 | repoStatus := &github.RepoStatus{} 134 | repoStatus.State = state 135 | 136 | if *ctx != "" { 137 | repoStatus.Context = ctx 138 | } 139 | if *description != "" { 140 | repoStatus.Description = description 141 | } 142 | if *url != "" { 143 | repoStatus.TargetURL = url 144 | } 145 | 146 | repoStatus, _, err := githubClient.Repositories.CreateStatus(context.Background(), *owner, *repo, *ref, repoStatus) 147 | if err != nil { 148 | log.Fatal(err) 149 | } 150 | 151 | fmt.Println("github-status-updater: Updated status", *repoStatus.ID) 152 | 153 | } else if *action == "update_branch_protection" { 154 | if *ref == "" { 155 | flag.PrintDefaults() 156 | log.Fatal("-ref or GITHUB_REF is required and must be a branch name") 157 | } 158 | if *ctx == "" { 159 | flag.PrintDefaults() 160 | log.Fatal("-context or GITHUB_CONTEXT required") 161 | } 162 | 163 | // https://godoc.org/github.com/google/go-github/github#RepositoriesService.GetBranchProtection 164 | // Get the existing branch protection and copy all the fields (to not override them), add the provided context to the existing contexts 165 | protection, _, err := githubClient.Repositories.GetBranchProtection(context.Background(), *owner, *repo, *ref) 166 | if err != nil { 167 | log.Fatal(err) 168 | } 169 | 170 | protectionRequest := &github.ProtectionRequest{} 171 | 172 | var requiredStatusChecks *github.RequiredStatusChecks 173 | var requiredPullRequestReviews *github.PullRequestReviewsEnforcement 174 | var enforceAdmins *github.AdminEnforcement 175 | var restrictions *github.BranchRestrictions 176 | 177 | if protection != nil { 178 | requiredStatusChecks = protection.GetRequiredStatusChecks() 179 | requiredPullRequestReviews = protection.GetRequiredPullRequestReviews() 180 | enforceAdmins = protection.GetEnforceAdmins() 181 | restrictions = protection.GetRestrictions() 182 | } 183 | 184 | if requiredStatusChecks == nil { 185 | requiredStatusChecks = &github.RequiredStatusChecks{Strict: true, Contexts: []string{}} 186 | } 187 | 188 | protectionRequest.RequiredStatusChecks = &github.RequiredStatusChecks{Strict: requiredStatusChecks.Strict, Contexts: append(requiredStatusChecks.Contexts, *ctx)} 189 | 190 | if requiredPullRequestReviews != nil { 191 | pullRequestReviewsEnforcementRequest := &github.PullRequestReviewsEnforcementRequest{} 192 | 193 | users := getUserLogins(requiredPullRequestReviews.DismissalRestrictions.Users) 194 | teams := getTeamSlugs(requiredPullRequestReviews.DismissalRestrictions.Teams) 195 | 196 | dismissalRestrictionsRequest := &github.DismissalRestrictionsRequest{ 197 | Users: &users, 198 | Teams: &teams, 199 | } 200 | 201 | pullRequestReviewsEnforcementRequest.DismissalRestrictionsRequest = dismissalRestrictionsRequest 202 | pullRequestReviewsEnforcementRequest.DismissStaleReviews = requiredPullRequestReviews.DismissStaleReviews 203 | pullRequestReviewsEnforcementRequest.RequireCodeOwnerReviews = requiredPullRequestReviews.RequireCodeOwnerReviews 204 | pullRequestReviewsEnforcementRequest.RequiredApprovingReviewCount = requiredPullRequestReviews.RequiredApprovingReviewCount 205 | protectionRequest.RequiredPullRequestReviews = pullRequestReviewsEnforcementRequest 206 | } 207 | 208 | if enforceAdmins != nil { 209 | protectionRequest.EnforceAdmins = enforceAdmins.Enabled 210 | } 211 | 212 | if restrictions != nil { 213 | branchRestrictionsRequest := &github.BranchRestrictionsRequest{Users: getUserLogins(restrictions.Users), Teams: getTeamSlugs(restrictions.Teams)} 214 | protectionRequest.Restrictions = branchRestrictionsRequest 215 | } 216 | 217 | // https://godoc.org/github.com/google/go-github/github#RepositoriesService.UpdateBranchProtection 218 | _, _, err = githubClient.Repositories.UpdateBranchProtection(context.Background(), *owner, *repo, *ref, protectionRequest) 219 | if err != nil { 220 | log.Fatal(err) 221 | } 222 | 223 | fmt.Println("github-status-updater: Updated branch protection") 224 | } 225 | } 226 | --------------------------------------------------------------------------------