diff --git a/.github/ISSUE_TEMPLATE/bug.yml b/.github/ISSUE_TEMPLATE/bug.yml
new file mode 100644
index 0000000..a35b724
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/bug.yml
@@ -0,0 +1,41 @@
+name: 'Bug report'
+description: 'File a bug report'
+labels: ['bug']
+body:
+- type: 'markdown'
+ attributes:
+ value: |-
+ Thank you for filing an bug. Please complete the form below so we can triage, reproduce, and fix your issue.
+
+- id: 'tldr'
+ type: 'textarea'
+ attributes:
+ label: 'TL;DR'
+ description: 'Describe the bug in 1-2 sentences.'
+ validations:
+ required: true
+
+- id: 'expected_behavior'
+ type: 'textarea'
+ attributes:
+ label: 'Expected behavior'
+ description: 'What did you expect to happen?'
+
+- id: 'observed_behavior'
+ type: 'textarea'
+ attributes:
+ label: 'Observed behavior'
+ description: 'What happened instead?'
+
+- id: 'log_output'
+ type: 'textarea'
+ attributes:
+ label: 'Debug log output'
+ description: 'Paste the debug log output here. See https://github.com/GoogleCloudPlatform/gcr-cleaner#Debugging for more information.'
+ render: 'text'
+
+- id: 'additional'
+ type: 'textarea'
+ attributes:
+ label: 'Additional information'
+ description: 'Is there anything else you think we should know?'
diff --git a/README.md b/README.md
index 1cfef4e..b5e4a13 100644
--- a/README.md
+++ b/README.md
@@ -1,177 +1,36 @@
# GCR Cleaner
-GCR Cleaner deletes stale images in Google Cloud [Container
-Registry][container-registry] or Google Cloud [Artifact
-Registry][artifact-registry]. This can help reduce costs and keep your container
-images list in order.
+GCR Cleaner deletes old container images in [Docker Hub][docker-hub], [Container Registry][container-registry], [Artifact Registry][artifact-registry], or any Docker v2 registries. This can help reduce storage costs, especially in CI/CD environments where images are created and pushed frequently.
-GCR Cleaner is designed to be deployed as a [Cloud Run][cloud-run] service and
-invoked periodically via [Cloud Scheduler][cloud-scheduler].
+There are multiple deployment options for GCR Cleaner. Click on your preferred
+deployment option for a detailed guide:
+
+- [Scheduled GitHub Action workflow](docs/deploy-github-actions.md)
+- [Deployed to Cloud Run](docs/deploy-cloud-run.md)
+
+For one-off tasks, you can also run GCR Cleaner locally:
```text
-+-------------------+ +-------------+ +-------+
-| Cloud Scheduler | -> | Cloud Run | -> | GCR |
-+-------------------+ +-------------+ +-------+
+docker run -it us-docker.pkg.dev/gcr-cleaner/gcr-cleaner/gcr-cleaner-cli
```
**This is not an official Google product.**
-## Setup
-
-1. Install the [Cloud SDK][cloud-sdk] for your operating system. Alternatively,
- you can run these commands from [Cloud Shell][cloud-shell], which has the SDK
- and other popular tools pre-installed.
-
-1. Export your project ID as an environment variable. The rest of this setup
- assumes this environment variable is set.
-
- ```sh
- export PROJECT_ID="my-project"
- ```
-
- Note this is your project _ID_, not the project _number_ or _name_.
-
-1. Enable the Google APIs - this only needs to be done once per project:
-
- ```sh
- gcloud services enable --project "${PROJECT_ID}" \
- appengine.googleapis.com \
- cloudscheduler.googleapis.com \
- run.googleapis.com
- ```
-
- This operation can take a few minutes, especially for recently-created
- projects.
-
-1. Create a service account which will be assigned to the Cloud Run service:
+## Container images
- ```sh
- gcloud iam service-accounts create "gcr-cleaner" \
- --project "${PROJECT_ID}" \
- --display-name "gcr-cleaner"
- ```
-
-1. Deploy the `gcr-cleaner` container on Cloud Run running as the service
- account just created:
-
- ```sh
- gcloud --quiet run deploy "gcr-cleaner" \
- --async \
- --project ${PROJECT_ID} \
- --platform "managed" \
- --service-account "gcr-cleaner@${PROJECT_ID}.iam.gserviceaccount.com" \
- --image "us-docker.pkg.dev/gcr-cleaner/gcr-cleaner/gcr-cleaner" \
- --region "us-central1" \
- --timeout "60s"
- ```
-
-1. Grant the service account access to delete references.
-
- - For **Container Registry**:
-
- ```sh
- gsutil acl ch -u gcr-cleaner@${PROJECT_ID}.iam.gserviceaccount.com:W gs://artifacts.${PROJECT_ID}.appspot.com
- ```
+Pre-built container images are available at the following locations. We do not
+offer versioned container images.
- To cleanup refs in _other_ GCP projects, replace `PROJECT_ID` with the
- target project ID. For example, if the Cloud Run service was running in
- "project-a" and you wanted to grant it permission to cleanup refs in
- "gcr.io/project-b/image", you would need to grant the Cloud Run service
- account in project-a permission on `artifacts.projects-b.appspot.com`.
-
- To clean up Container Registry images hosted in specific regions, update the bucket name to include the region:
-
- ```text
- gs://eu.artifacts.${PROJECT_ID}.appspot.com
- ```
-
- If you plan on using the `recursive` functionality, you must also grant the
- service account "Browser" permissions:
-
- ```sh
- gcloud projects add-iam-policy-binding "${PROJECT_ID}" \
- --member "serviceAccount:gcr-cleaner@${PROJECT_ID}.iam.gserviceaccount.com" \
- --role "roles/browser"
- ```
-
- - For **Artifact Registry**:
-
- ```sh
- gcloud artifacts repositories add-iam-policy-binding "${REPO_NAME}" \
- --project "${PROJECT_ID}" \
- --location "${LOCATION}" \
- --member "serviceAccount:gcr-cleaner@${PROJECT_ID}.iam.gserviceaccount.com" \
- --role "roles/artifactregistry.repoAdmin"
- ```
-
- Where "REPO_NAME" is the name of the Artifact Registry repository and
- "LOCATION" is the geographic location.
-
-1. Create a service account with permission to invoke the Cloud Run service:
+```text
+gcr.io/gcr-cleaner/gcr-cleaner
+asia-docker.pkg.dev/gcr-cleaner/gcr-cleaner/gcr-cleaner
+europe-docker.pkg.dev/gcr-cleaner/gcr-cleaner/gcr-cleaner
+us-docker.pkg.dev/gcr-cleaner/gcr-cleaner/gcr-cleaner
+```
- ```sh
- gcloud iam service-accounts create "gcr-cleaner-invoker" \
- --project "${PROJECT_ID}" \
- --display-name "gcr-cleaner-invoker"
- ```
- ```sh
- gcloud run services add-iam-policy-binding "gcr-cleaner" \
- --project "${PROJECT_ID}" \
- --platform "managed" \
- --region "us-central1" \
- --member "serviceAccount:gcr-cleaner-invoker@${PROJECT_ID}.iam.gserviceaccount.com" \
- --role "roles/run.invoker"
- ```
-
-1. Create a Cloud Scheduler HTTP job to invoke the function every week:
-
- ```sh
- gcloud app create \
- --project "${PROJECT_ID}" \
- --region "us-central" \
- --quiet
- ```
-
- ```sh
- # Replace this with the full name of the repository for which you
- # want to cleanup old references, for example:
- export REPO="gcr.io/${PROJECT_ID}/my-image"
- export REPO="us-docker-pkg.dev/${PROJECT_ID}/my-repo/my-image"
- ```
-
- ```sh
- # Capture the URL of the Cloud Run service:
- export SERVICE_URL=$(gcloud run services describe gcr-cleaner --project "${PROJECT_ID}" --platform "managed" --region "us-central1" --format 'value(status.url)')
- ```
-
- ```sh
- gcloud scheduler jobs create http "gcrclean-myimage" \
- --project ${PROJECT_ID} \
- --description "Cleanup ${REPO}" \
- --uri "${SERVICE_URL}/http" \
- --message-body "{\"repos\":[\"${REPO}\"]}" \
- --oidc-service-account-email "gcr-cleaner-invoker@${PROJECT_ID}.iam.gserviceaccount.com" \
- --schedule "0 8 * * 2" \
- --time-zone="US/Eastern"
- ```
-
- You can create specify multiple repositories in the list to clean more than
- one repository.
-
-1. _(Optional)_ Run the scheduled job now:
-
- ```sh
- gcloud scheduler jobs run "gcrclean-myimage" \
- --project "${PROJECT_ID}"
- ```
-
- Note: for initial job deployments, you must wait a few minutes before
- invoking.
-
-
-## Payload & Parameters
+## Payload & parameters
The payload is expected to be JSON with the following fields:
@@ -252,62 +111,69 @@ The payload is expected to be JSON with the following fields:
these will enable deletion by tag.
-## Running locally
+## Permissions
-In addition to the server, you can also run GCR Cleaner locally for one-off tasks using `cmd/gcr-cleaner-cli`:
+This section lists the minimum required permissions depending on the target
+cleanup system.
-```text
-docker run -it us-docker.pkg.dev/gcr-cleaner/gcr-cleaner/gcr-cleaner-cli
-```
+#### Artifact Registry
-## Logging
+The service account running GCR cleaner must have
+`roles/artifactregistry.repoAdmin` or greater on the Artifact Registry
+repositories. Here is an example for setting that permissions via `gcloud`:
-By default, logs are produced at the `info` level. More logs are available at
-the `debug` level. To set the log level, set `GCRCLEANER_LOG=debug`.
+```sh
+gcloud artifacts repositories add-iam-policy-binding "my-repo" \
+ --project "my-project" \
+ --location "us" \
+ --member "serviceAccount:gcr-cleaner@my-project.iam.gserviceaccount.com" \
+ --role "roles/artifactregistry.repoAdmin"
+```
+#### Container Registry
-## I just want the container!
+Container Registry stores images in Google Cloud Storage, so the service account
+running GCR Cleaner must have read and write permissions on the underlying Cloud
+Storage bucket. Here is an example for setting that permission via `gsutil`:
+
+```sh
+gsutil acl ch -u gcr-cleaner@my-project.iam.gserviceaccount.com:W gs://artifacts.my-project.appspot.com
+```
-You can build the container yourself using the included Dockerfile.
-Alternatively, you can source a pre-built container from Artifact Registry or
-Container Registry. All of the following URLs provide an equivalent image:
+To clean up Container Registry images hosted in specific regions, update the
+bucket name to include the region:
```text
-gcr.io/gcr-cleaner/gcr-cleaner
-asia-docker.pkg.dev/gcr-cleaner/gcr-cleaner/gcr-cleaner
-europe-docker.pkg.dev/gcr-cleaner/gcr-cleaner/gcr-cleaner
-us-docker.pkg.dev/gcr-cleaner/gcr-cleaner/gcr-cleaner
+gs://eu.artifacts.my-project.appspot.com
```
-## What about using Terraform!
+If you plan on using the `recursive` functionality, you must also grant the
+service account "Browser" permissions:
-:package: You can deploy the stack using the community-supported Terraform module [gcr-cleaner](https://registry.terraform.io/modules/mirakl/gcr-cleaner/google/latest#usage):
+```sh
+gcloud projects add-iam-policy-binding "my-project" \
+ --member "serviceAccount:gcr-cleaner@my-project.iam.gserviceaccount.com" \
+ --role "roles/browser"
+```
-## FAQ
-**Does it work with Cloud Pub/Sub?**
-
-Yes! Just change the endpoint from `/http` to `/pubsub`!
+## Debugging
+
+By default, GCR Cleaner only emits user-level logging at the "info" level. More logs are available at the "debug" level. To configure the log level, set the `GCRCLEANER_LOG` environment variable to the desired log value:
+
+```sh
+export GCRCLEANER_LOG=debug
+```
+
+In debug mode, GCR Cleaner will print **a lot** of information, including its
+entire decision process for candidate deletion. If you open an issue, please
+include these debug logs as they are very helpful in finding and fixing any
+bugs.
-**What was your inspiration?**
-
-GCR Cleaner is largely inspired by [ahmetb](https://twitter.com/ahmetb)'s
-[gcrgc.sh][gcrgc.sh], but it is written in Go and is designed to be run as a
-service.
-## License
-This library is licensed under Apache 2.0. Full license text is available in
-[LICENSE](https://github.com/GoogleCloudPlatform/gcr-cleaner/tree/master/LICENSE).
[artifact-registry]: https://cloud.google.com/artifact-registry
-[cloud-build]: https://cloud.google.com/build/
-[cloud-pubsub]: https://cloud.google.com/pubsub/
-[cloud-run]: https://cloud.google.com/run/
-[cloud-scheduler]: https://cloud.google.com/scheduler/
-[cloud-sdk]: https://cloud.google.com/sdk
-[cloud-shell]: https://cloud.google.com/shell
[container-registry]: https://cloud.google.com/container-registry
-[gcr-cleaner-godoc]: https://godoc.org/github.com/GoogleCloudPlatform/gcr-cleaner/pkg/gcrcleaner
-[gcrgc.sh]: https://gist.github.com/ahmetb/7ce6d741bd5baa194a3fac6b1fec8bb7
+[docker-hub]: https://hub.docker.com
[go-re]: https://golang.org/pkg/regexp/syntax/
diff --git a/docs/deploy-cloud-run.md b/docs/deploy-cloud-run.md
new file mode 100644
index 0000000..3c43754
--- /dev/null
+++ b/docs/deploy-cloud-run.md
@@ -0,0 +1,124 @@
+# Deploy GCR Cleaner to Cloud Run
+
+This document describes how to deploy GCR Cleaner to Cloud Run [Cloud
+Run][cloud-run] and invoke it via [Cloud Scheduler][cloud-scheduler]. There is
+also a community-supported [Terraform module for gcr-cleaner][tf-module].
+
+
+1. Install the [Cloud SDK][cloud-sdk] for your operating system. Alternatively,
+ you can run these commands from [Cloud Shell][cloud-shell], which has the SDK
+ and other popular tools pre-installed.
+
+1. Export your project ID as an environment variable. The rest of this setup
+ assumes this environment variable is set.
+
+ ```sh
+ export PROJECT_ID="my-project"
+ ```
+
+ Note this is your project _ID_, not the project _number_ or _name_.
+
+1. Enable the Google APIs - this only needs to be done once per project:
+
+ ```sh
+ gcloud services enable --project "${PROJECT_ID}" \
+ appengine.googleapis.com \
+ cloudscheduler.googleapis.com \
+ run.googleapis.com
+ ```
+
+ This operation can take a few minutes, especially for recently-created
+ projects.
+
+1. Create a service account which will be assigned to the Cloud Run service:
+
+ ```sh
+ gcloud iam service-accounts create "gcr-cleaner" \
+ --project "${PROJECT_ID}" \
+ --display-name "gcr-cleaner"
+ ```
+
+1. Deploy the `gcr-cleaner` container on Cloud Run running as the service
+ account just created:
+
+ ```sh
+ gcloud --quiet run deploy "gcr-cleaner" \
+ --async \
+ --project ${PROJECT_ID} \
+ --platform "managed" \
+ --service-account "gcr-cleaner@${PROJECT_ID}.iam.gserviceaccount.com" \
+ --image "us-docker.pkg.dev/gcr-cleaner/gcr-cleaner/gcr-cleaner" \
+ --region "us-central1" \
+ --timeout "60s"
+ ```
+
+1. Grant the service account access to delete references. See
+ [Permissions](../README.md#permissions) for more information.
+
+1. Create a service account with permission to invoke the Cloud Run service:
+
+ ```sh
+ gcloud iam service-accounts create "gcr-cleaner-invoker" \
+ --project "${PROJECT_ID}" \
+ --display-name "gcr-cleaner-invoker"
+ ```
+
+ ```sh
+ gcloud run services add-iam-policy-binding "gcr-cleaner" \
+ --project "${PROJECT_ID}" \
+ --platform "managed" \
+ --region "us-central1" \
+ --member "serviceAccount:gcr-cleaner-invoker@${PROJECT_ID}.iam.gserviceaccount.com" \
+ --role "roles/run.invoker"
+ ```
+
+1. Create a Cloud Scheduler HTTP job to invoke the function every week:
+
+ ```sh
+ gcloud app create \
+ --project "${PROJECT_ID}" \
+ --region "us-central" \
+ --quiet
+ ```
+
+ ```sh
+ # Replace this with the full name of the repository for which you
+ # want to cleanup old references, for example:
+ export REPO="gcr.io/${PROJECT_ID}/my-image"
+ export REPO="us-docker-pkg.dev/${PROJECT_ID}/my-repo/my-image"
+ ```
+
+ ```sh
+ # Capture the URL of the Cloud Run service:
+ export SERVICE_URL=$(gcloud run services describe gcr-cleaner --project "${PROJECT_ID}" --platform "managed" --region "us-central1" --format 'value(status.url)')
+ ```
+
+ ```sh
+ gcloud scheduler jobs create http "gcrclean-myimage" \
+ --project ${PROJECT_ID} \
+ --description "Cleanup ${REPO}" \
+ --uri "${SERVICE_URL}/http" \
+ --message-body "{\"repos\":[\"${REPO}\"]}" \
+ --oidc-service-account-email "gcr-cleaner-invoker@${PROJECT_ID}.iam.gserviceaccount.com" \
+ --schedule "0 8 * * 2" \
+ --time-zone="US/Eastern"
+ ```
+
+ You can create specify multiple repositories in the list to clean more than
+ one repository.
+
+1. _(Optional)_ Run the scheduled job now:
+
+ ```sh
+ gcloud scheduler jobs run "gcrclean-myimage" \
+ --project "${PROJECT_ID}"
+ ```
+
+ Note: for initial job deployments, you must wait a few minutes before
+ invoking.
+
+[cloud-run]: https://cloud.google.com/run/
+[cloud-scheduler]: https://cloud.google.com/scheduler/
+[cloud-sdk]: https://cloud.google.com/sdk
+[cloud-shell]: https://cloud.google.com/shell
+[tf-module]: https://registry.terraform.io/modules/mirakl/gcr-cleaner/google/latest#usage
diff --git a/docs/deploy-github-actions.md b/docs/deploy-github-actions.md
new file mode 100644
index 0000000..9090c61
--- /dev/null
+++ b/docs/deploy-github-actions.md
@@ -0,0 +1,54 @@
+# Deploy GCR Cleaner to GitHub Actions
+
+This document describes how to invoke GCR Cleaner via cron in GitHub Actions.
+[GitHub Actions][github-actions] is a CI/CD solution provided by GitHub, and it
+is free for open source projects. There are multiple triggers for GitHub Actions
+worklflows, including [cron scheduler][github-actions-cron].
+
+The easiest way to use GCR Cleaner with GitHub Actions is via the pre-built
+`gcr-cleaner-cli` container and a scheduled cron GitHub Actions workflow.
+
+```yaml
+# .github/workflows/gcr-cleaner.yml
+name: 'gcr-cleaner'
+
+on:
+ schedule:
+ - cron: '0 0 */1 * *' # runs daily
+ workflow_dispatch: # allows for manual invocation
+
+jobs:
+ gcr-cleaner:
+ runs-on: 'ubuntu-latest'
+ steps:
+ # configure based on your registry
+ - uses: 'docker/login-action@v2'
+ with:
+ username: ${{ secrets.DOCKERHUB_USERNAME }}
+ password: ${{ secrets.DOCKERHUB_PASSWORD }}
+
+ # customize based on the gcr-cleaner flags
+ - uses: 'docker://us-docker.pkg.dev/gcr-cleaner/gcr-cleaner/gcr-cleaner-cli'
+ with:
+ args: >-
+ -repo=us-docker.pkg.dev/my-repo/my-thing
+ -repo=ghcr.io/myuser/my-image
+ -grace=48h
+```
+
+
+## Authentication
+
+In order to actually delete images in the upstream registry, you will need to
+authenticate to the upstream registry. The easiest way to authenticate is to use
+the [docker/login-action][docker-auth]. The [documentation][docker-auth] has
+detailed configuration instructions for other types of repositories.
+
+You must grant the authenticated principal permission to read and delete
+resources in the registry. See [Permissions](../README.md#permissions) for more
+information.
+
+
+[github-actions]: https://github.com/features/actions
+[github-actions-cron]: https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#schedule
+[docker-auth]: https://github.com/docker/login-action