Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ jobs:
build:
name: Build, test, check
runs-on: ubuntu-latest
env:
CARGO_REGISTRIES_GEN0SEC_INDEX: sparse+https://crates-internal.g0s.dev/api/v1/crates/
CARGO_REGISTRIES_GEN0SEC_TOKEN: ${{ secrets.GEN0SEC_CARGO_TOKEN }}
CARGO_REGISTRIES_GEN0SEC_CREDENTIAL_PROVIDER: cargo:token
steps:
- uses: actions/checkout@v6
- uses: actions-rs/toolchain@v1
Expand Down
107 changes: 107 additions & 0 deletions .github/workflows/release.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
name: Release
on:
push:
tags: ['v*']
workflow_dispatch:
inputs:
dry-run:
description: 'Run cargo publish with --dry-run (skips registry push and GH release)'
type: boolean
default: false

jobs:
verify-version:
if: ${{ startsWith(github.ref, 'refs/tags/v') }}
runs-on: ubuntu-latest
outputs:
version: ${{ steps.v.outputs.version }}
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- id: v
run: |
TAG_VERSION="${GITHUB_REF_NAME#v}"
CRATE_VERSION=$(grep -m1 '^version' Cargo.toml | sed -E 's/.*"([^"]+)".*/\1/')
if [ "$TAG_VERSION" != "$CRATE_VERSION" ]; then
echo "Tag version ($TAG_VERSION) does not match Cargo.toml version ($CRATE_VERSION)" >&2
exit 1
fi
echo "version=$TAG_VERSION" >> "$GITHUB_OUTPUT"

package-crate:
needs: [verify-version]
runs-on: ubuntu-latest
permissions:
contents: read
env:
CARGO_REGISTRIES_GEN0SEC_INDEX: sparse+https://crates-internal.g0s.dev/api/v1/crates/
CARGO_REGISTRIES_GEN0SEC_TOKEN: ${{ secrets.GEN0SEC_CARGO_TOKEN }}
CARGO_REGISTRIES_GEN0SEC_CREDENTIAL_PROVIDER: cargo:token
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8 # stable
- uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2
- name: cargo package
run: cargo package --registry gen0sec --locked
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
with:
name: proxy-protocol-crate
path: target/package/proxy-protocol-${{ needs.verify-version.outputs.version }}.crate
if-no-files-found: error
retention-days: 7

publish:
needs: [verify-version, package-crate]
runs-on: ubuntu-latest
environment: release
permissions:
contents: read
env:
CARGO_REGISTRIES_GEN0SEC_INDEX: sparse+https://crates-internal.g0s.dev/api/v1/crates/
CARGO_REGISTRIES_GEN0SEC_TOKEN: ${{ secrets.GEN0SEC_CARGO_TOKEN }}
CARGO_REGISTRIES_GEN0SEC_CREDENTIAL_PROVIDER: cargo:token
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8 # stable
- uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2
- name: Cargo publish
env:
DRY_RUN: ${{ inputs.dry-run }}
CARGO_HTTP_TIMEOUT: '300'
CARGO_HTTP_LOW_SPEED_LIMIT: '1'
CARGO_NET_RETRY: '5'
run: |
ARGS="--registry gen0sec --locked"
if [ "$DRY_RUN" = "true" ]; then
ARGS="$ARGS --dry-run"
fi
for attempt in 1 2 3; do
if cargo publish $ARGS; then exit 0; fi
echo "Publish attempt $attempt failed, retrying in 15s..."
sleep 15
done
echo "Publish failed after 3 attempts" >&2
exit 1

gh-release:
needs: [verify-version, package-crate, publish]
if: ${{ startsWith(github.ref, 'refs/tags/v') && inputs.dry-run != true }}
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
with:
name: proxy-protocol-crate
path: release-assets
- name: SHA256 checksums
run: |
set -euo pipefail
cd release-assets
for f in *; do [ -f "$f" ] && sha256sum "$f" > "${f}.sha256"; done
ls -la
- uses: softprops/action-gh-release@b4309332981a82ec1c5618f44dd2e27cc8bfbfda # v3.0.0
with:
draft: false
generate_release_notes: true
files: release-assets/*
make_latest: true
32 changes: 32 additions & 0 deletions .github/workflows/wellness-check.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
name: Wellness Check
on:
pull_request:
branches: [main, master]

permissions:
contents: read

jobs:
fmt-and-test:
runs-on: ubuntu-latest
env:
CARGO_REGISTRIES_GEN0SEC_INDEX: sparse+https://crates-internal.g0s.dev/api/v1/crates/
CARGO_REGISTRIES_GEN0SEC_TOKEN: ${{ secrets.GEN0SEC_CARGO_TOKEN }}
CARGO_REGISTRIES_GEN0SEC_CREDENTIAL_PROVIDER: cargo:token
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6

- uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8 # stable
with:
components: rustfmt, clippy

- uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2

- name: Check formatting
run: cargo fmt --all -- --check

- name: Check clippy
run: cargo clippy --all-targets --all-features -- --deny warnings

- name: Run tests
run: cargo test -- --include-ignored
8 changes: 8 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,14 @@ license = "MIT OR Apache-2.0"
description = "PROXY protocol serializer and deserializer"
documentation = "https://docs.rs/proxy-protocol/"
repository = "https://github.com/Proximyst/proxy-protocol.git"
keywords = ["proxy", "proxy-protocol", "haproxy", "networking"]
categories = ["network-programming", "parser-implementations"]
links = "proxy-protocol"
readme = "README.md"

[package.metadata.release]
publish = false
tag-name = "v{{version}}"

[dependencies]
snafu = "~0.9"
Expand Down
51 changes: 51 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,54 @@ additional terms or conditions.
[LICENCE-APACHE-2.0]: ./LICENCE-APACHE-2.0
[Apache-2.0]: https://www.apache.org/licenses/LICENSE-2.0
[LICENCE-MIT]: ./LICENCE-MIT

## Release Process

`proxy-protocol` is published to the private `gen0sec` Cargo registry
(`https://crates-internal.g0s.dev`). Releases are driven by `vX.Y.Z` git
tags — `.github/workflows/release.yaml` handles build, publish, and
GitHub Release creation.

### One-time setup

- **Repo secret**: `GEN0SEC_CARGO_TOKEN` — bearer token for the registry.
- **`release` GitHub environment** (Settings → Environments → New) for
optional manual approval gating before publish.
- **Local cargo config** (`~/.cargo/config.toml`) for developers:

```toml
[registries.gen0sec]
index = "sparse+https://crates-internal.g0s.dev/api/v1/crates/"
credential-provider = ["cargo:token"]
token = "<your_token>"
```

### Cutting a release

```bash
cargo install cargo-release
cargo release patch --execute # or minor / major / 1.2.3
```

`cargo-release` bumps `Cargo.toml` + `Cargo.lock`, commits, creates
`vX.Y.Z`, and pushes; CI publishes on the tag push.
`[package.metadata.release] publish = false` keeps the local command
from publishing — that is left to CI.

### CI jobs on `v*` tag

1. **`verify-version`** — fails if tag does not match `Cargo.toml`.
2. **`package-crate`** — `cargo package --registry gen0sec --locked`,
uploads `proxy-protocol-X.Y.Z.crate`.
3. **`publish`** — `release` environment-gated `cargo publish` with
retry/timeout hardening.
4. **`gh-release`** — downloads `.crate`, generates SHA256 sidecars,
creates a GitHub Release.

### Required Cargo.toml metadata

The gen0sec registry enforces these `[package]` fields, missing ones
cause a `400 Bad Request`:

`name`, `description`, `repository`, `license`, `authors`, `categories`,
`keywords`, `links`, `readme`.
3 changes: 3 additions & 0 deletions build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
fn main() {
println!("cargo:rerun-if-changed=build.rs");
}
10 changes: 5 additions & 5 deletions src/version1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -410,7 +410,7 @@ mod encode_tests {
#[test]
fn test_unknown() {
let encoded = encode(ProxyAddresses::Unknown);
assert!(matches!(encoded, Ok(_)));
assert!(encoded.is_ok());
assert_eq!(encoded.unwrap(), &b"PROXY UNKNOWN\r\n"[..]);
}

Expand All @@ -420,7 +420,7 @@ mod encode_tests {
source: SocketAddrV4::new(Ipv4Addr::new(1, 2, 3, 4), 987),
destination: SocketAddrV4::new(Ipv4Addr::new(255, 254, 253, 252), 12345),
});
assert!(matches!(encoded, Ok(_)));
assert!(encoded.is_ok());
assert_eq!(
encoded.unwrap(),
Bytes::from_static(&b"PROXY TCP4 1.2.3.4 255.254.253.252 987 12345\r\n"[..]),
Expand All @@ -438,7 +438,7 @@ mod encode_tests {
0,
),
});
assert!(matches!(encoded, Ok(_)));
assert!(encoded.is_ok());
assert_eq!(
encoded.unwrap(),
Bytes::from_static(
Expand All @@ -455,7 +455,7 @@ mod encode_tests {
0,
),
});
assert!(matches!(encoded, Ok(_)));
assert!(encoded.is_ok());
assert_eq!(
encoded.unwrap(),
Bytes::from_static(&b"PROXY TCP6 1:2:3:4:5:6:7:8 ffff:fffe::1:2:3 987 12345\r\n"[..]),
Expand All @@ -465,7 +465,7 @@ mod encode_tests {
source: SocketAddrV6::new(Ipv6Addr::new(1, 2, 3, 4, 5, 6, 7, 8), 987, 0, 0),
destination: SocketAddrV6::new(Ipv6Addr::new(0, 0, 0, 0, 0, 1, 2, 3), 12345, 0, 0),
});
assert!(matches!(encoded, Ok(_)));
assert!(encoded.is_ok());
assert_eq!(
encoded.unwrap(),
Bytes::from_static(&b"PROXY TCP6 1:2:3:4:5:6:7:8 ::1:2:3 987 12345\r\n"[..]),
Expand Down
Loading