chore: automate releases (#189)

This also fixes our current dependency on GitHub archives which are not
guaranteed to be stable,
https://github.com/bazelbuild/bazel-central-registry/pull/332/files#diff-7288b02983f19cbe12177d5f193a6bfc533e352fc44fbe12cdb93e1c8c28dcd1R4
which requires a special 'skip-url-stability-check' label on the BCR PR

Instead, we run 'git archive' to create the release artifact. We can now
prune the tests/ folder from that artifact, which lets us remove an
exclusion from the presubmit as well.

Fixes #188
diff --git a/.bcr/README.md b/.bcr/README.md
new file mode 100644
index 0000000..706f926
--- /dev/null
+++ b/.bcr/README.md
@@ -0,0 +1,9 @@
+# Bazel Central Registry
+
+When the ruleset is released, we want it to be published to the
+Bazel Central Registry automatically:
+<https://registry.bazel.build>
+
+This folder contains configuration files to automate the publish step.
+See <https://github.com/bazel-contrib/publish-to-bcr/blob/main/templates/README.md>
+for authoritative documentation about these files.
\ No newline at end of file
diff --git a/.bcr/metadata.template.json b/.bcr/metadata.template.json
index 024f7a4..79772a7 100644
--- a/.bcr/metadata.template.json
+++ b/.bcr/metadata.template.json
@@ -1,8 +1,26 @@
 {
   "homepage": "https://github.com/bazelbuild/rules_proto",
-  "maintainers": [],
+  "maintainers": [
+    {
+      "github": "googleberg",
+      "name": "Jerry Berg"
+    },
+    {
+      "github": "zhangskz"
+    },
+    {
+      "email": "alex@aspect.dev",
+      "github": "alexeagle",
+      "name": "Alex Eagle"
+    },
+    {
+      "email": "sahin@aspect.dev",
+      "github": "thesayyn",
+      "name": "Şahin Yort"
+    }
+  ],
   "versions": [],
-  "yanked_versions": {}
+  "yanked_versions": {},
   "repository": [
     "github:bazelbuild/rules_proto"
   ]
diff --git a/.bcr/source.template.json b/.bcr/source.template.json
index 4f14819..7fb5985 100644
--- a/.bcr/source.template.json
+++ b/.bcr/source.template.json
@@ -1,5 +1,5 @@
 {
-  "integrity": "",
+  "integrity": "**leave this alone**",
   "strip_prefix": "{REPO}-{VERSION}",
-  "url": "https://github.com/{OWNER}/{REPO}/archive/refs/tags/{TAG}.tar.gz"
+  "url": "https://github.com/{OWNER}/{REPO}/releases/download/{TAG}/{REPO}-{TAG}.tar.gz"
 }
diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000..3194ddf
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,7 @@
+# In code review, collapse generated files
+docs/*.md linguist-generated=true
+
+# Configuration for 'git archive'
+# see https://git-scm.com/docs/git-archive/2.40.0#ATTRIBUTES
+# Omit folders that users don't need, making the distribution artifact smaller
+tests export-ignore
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
new file mode 100644
index 0000000..43ea4de
--- /dev/null
+++ b/.github/workflows/release.yml
@@ -0,0 +1,18 @@
+# Automatically perform a release whenever a new "release-like" tag is pushed to the repo.
+name: Release
+
+on:
+  push:
+    tags:
+      # Detect tags that look like a release.
+      # Note that we don't use a "v" prefix to help anchor this pattern.
+      # This is purely a matter of preference.
+      - "*.*.*"
+
+jobs:
+  release:
+    # Re-use https://github.com/bazel-contrib/.github/blob/v5/.github/workflows/release_ruleset.yaml
+    uses: bazel-contrib/.github/.github/workflows/release_ruleset.yaml@v5
+    with:
+      prerelease: false
+      release_files: rules_proto-*.tar.gz
\ No newline at end of file
diff --git a/.github/workflows/release_prep.sh b/.github/workflows/release_prep.sh
new file mode 100644
index 0000000..8fc1749
--- /dev/null
+++ b/.github/workflows/release_prep.sh
@@ -0,0 +1,43 @@
+#!/usr/bin/env bash
+
+set -o errexit -o nounset -o pipefail
+
+# Set by GH actions, see
+# https://docs.github.com/en/actions/learn-github-actions/environment-variables#default-environment-variables
+readonly TAG=${GITHUB_REF_NAME}
+# The prefix is chosen to match what GitHub generates for source archives.
+# This guarantees that users can easily switch from a released artifact to a source archive
+# with minimal differences in their code (e.g. strip_prefix remains the same)
+readonly PREFIX="rules_proto-${TAG}"
+readonly ARCHIVE="${PREFIX}.tar.gz"
+
+# NB: configuration for 'git archive' is in /.gitattributes
+git archive --format=tar --prefix=${PREFIX}/ ${TAG} | gzip > $ARCHIVE
+SHA=$(shasum -a 256 $ARCHIVE | awk '{print $1}')
+
+# The stdout of this program will be used as the top of the release notes for this release.
+cat << EOF
+## Using bzlmod with Bazel 6 or later:
+
+1. [Bazel 6] Add \`common --enable_bzlmod\` to \`.bazelrc\`.
+
+2. Add to your \`MODULE.bazel\` file:
+
+\`\`\`starlark
+bazel_dep(name = "rules_proto", version = "${TAG}")
+\`\`\`
+
+## Using WORKSPACE:
+
+\`\`\`starlark
+
+load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
+
+http_archive(
+    name = "rules_proto",
+    sha256 = "${SHA}",
+    strip_prefix = "${PREFIX}",
+    url = "https://github.com/bazelbuild/rules_proto/releases/download/${TAG}/${ARCHIVE}",
+)
+\`\`\`starlark
+EOF
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 939e534..1c897d0 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -26,3 +26,13 @@
 
 This project follows [Google's Open Source Community
 Guidelines](https://opensource.google.com/conduct/).
+
+## Releasing
+
+To perform a release, simply tag the commit you wish to release, for example:
+
+```
+rules_proto$ git fetch
+rules_proto$ git tag 1.2.3 origin/main
+rules_proto$ git push origin 1.2.3
+```
\ No newline at end of file
diff --git a/MODULE.bazel b/MODULE.bazel
index aa9a96e..f34579f 100644
--- a/MODULE.bazel
+++ b/MODULE.bazel
@@ -1,6 +1,9 @@
+"Bazel dependencies"
+
 module(
     name = "rules_proto",
-    version = "5.3.0-21.7",
+    # Note: the publish-to-BCR app will patch this line to stamp the version being published.
+    version = "0.0.0",
     compatibility_level = 1,
 )