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
63 changes: 63 additions & 0 deletions .github/workflows/release.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
name: Release

on:
push:
tags: ["v*"]
workflow_dispatch:
inputs:
tag:
description: "Tag to release (e.g. v1.4.0)"
required: true
type: string

jobs:
ci:
name: CI
uses: ./.github/workflows/rust.yaml

debian-package:
name: Build
needs: ci
runs-on: ubuntu-latest
permissions:
contents: write
id-token: write
attestations: write
steps:
- uses: actions/checkout@v4
with:
ref: ${{ inputs.tag || github.ref }}

- uses: docker/setup-buildx-action@v3

- name: Build binary package
uses: docker/build-push-action@v6
with:
context: .
target: export
push: false
outputs: type=local,dest=dist
cache-from: type=gha
cache-to: type=gha,mode=max

- name: Attest build provenance
uses: actions/attest-build-provenance@v2
if: ${{ !github.event.repository.private }}
with:
subject-path: dist/*.deb

- name: Extract changelog
id: changelog
run: |
{
echo "body<<EOF"
dpkg-parsechangelog -S Changes | tail -n+4
echo "EOF"
} >> "$GITHUB_OUTPUT"

- name: Create GitHub Release
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ inputs.tag || github.ref_name }}
body: ${{ steps.changelog.outputs.body }}
files: dist/*.deb
59 changes: 59 additions & 0 deletions .github/workflows/rust.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
name: Rust

on:
push:
branches: [main]
pull_request:
branches: [main]
workflow_call:

env:
CARGO_TERM_COLOR: always

jobs:
check:
name: Check
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
with:
shared-key: cargo
- run: cargo check

test:
name: Test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
with:
shared-key: cargo
save-if: false
- run: cargo test

fmt:
name: Format
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
with:
components: rustfmt
- run: cargo fmt --all --check

clippy:
name: Clippy
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
with:
components: clippy
- uses: Swatinem/rust-cache@v2
with:
shared-key: cargo
save-if: false
- run: cargo clippy -- -D warnings
55 changes: 55 additions & 0 deletions .github/workflows/tag-release.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
name: Tag release

on:
workflow_dispatch:
inputs:
version:
description: "Version number (e.g. 1.4.0)"
required: true
type: string

jobs:
tag:
name: Tag release
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Validate version
run: |
if ! echo "${{ inputs.version }}" | grep -Pq '^\d+\.\d+\.\d+(~[a-z0-9.]+)?$'; then
echo "::error::Invalid version '${{ inputs.version }}'. Expected format: X.Y.Z or X.Y.Z~suffix"
exit 1
fi

- uses: actions/checkout@v4
with:
ref: main
token: ${{ secrets.APP_PRIVATE_KEY }}

- name: Install dependencies
run: sudo apt-get update && sudo apt-get install -y devscripts dpkg-dev

- name: Configure git
run: |
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"

- name: Finalize changelog
run: |
dch --controlmaint --newversion "${{ inputs.version }}" ''
dch --controlmaint --release ''

- name: Update Cargo.toml version
run: |
cargo_version="${{ inputs.version }}"
cargo_version="${cargo_version//\~/-}"
sed -i "s/^version = \".*\"/version = \"$cargo_version\"/" Cargo.toml
cargo update apt-transport-github

- name: Commit and tag
run: |
git add debian/changelog Cargo.toml Cargo.lock
git commit -m "Release v${{ inputs.version }}"
git tag "v${{ inputs.version }}"
git push origin main "v${{ inputs.version }}"
5 changes: 3 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM debian:latest AS build
FROM rust:latest AS build

RUN apt-get update && apt-get install -y \
build-essential \
Expand All @@ -17,7 +17,8 @@ RUN apt-get update \

# Copy the rest of the source code to the build the binary package
COPY . .
RUN dpkg-buildpackage -b -uc -us
RUN --mount=type=cache,target=/build/sources/target \
dpkg-buildpackage -b -uc -us

RUN mkdir /dist && mv ../*.deb /dist/

Expand Down
1 change: 1 addition & 0 deletions debian/apt-transport-github.install
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
target/release/github usr/lib/apt/methods/
8 changes: 8 additions & 0 deletions debian/rules
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,14 @@ MAN_PAGES := apt-transport-github.1
%:
dh $@

override_dh_auto_build:
cargo build --release

override_dh_auto_test:
cargo test --release

execute_before_dh_install:
cp target/release/apt-transport-github target/release/github

execute_before_dh_installman: man

Expand Down
8 changes: 1 addition & 7 deletions docs/ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,4 @@ APT will request for the method to obtain the `Packages` file from the repositor

Since this file isn't provided by GitHub Releases automatically, the transport method will synthesize one from information contained in the GitHub Release. This file is then stored in APT's variable state directory and parsed by it to determine packages available to install.

Each stanza in the `Packages` file has a `Filename` key, which this method sets to the download URL of that asset, which allows the method to fetch the asset when requested by APT.

```
https://api.github.com/repos/{owner}/{repo}/releases/assets/{asset_id}
```

The `{asset_id}` is found in the response to various [Releases REST API requests](https://docs.github.com/en/rest/releases/releases?apiVersion=2026-03-10). Each asset that has a `content_type` of `application/x-debian-package` is included.
Each stanza in the `Packages` file has a `Filename` key, which this method sets to the asset ID of that asset, which allows the method to fetch the asset when requested by APT. The asset ID is found in the response to various [Releases REST API requests](https://docs.github.com/en/rest/releases/releases?apiVersion=2026-03-10). Each asset that has a `content_type` of `application/x-debian-package` is included.
21 changes: 21 additions & 0 deletions docs/RELEASE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Release

1. Add changelog entries during development:

```bash
dch -M "Description of change"
```

1. Merge changes into `main`
1. Run the **Release** workflow from the Actions tab, providing the new version number

The workflow will finalize the changelog, update `Cargo.toml`, commit, tag, and push.
CI runs on the tag, and on success, CD builds the `.deb` and creates a GitHub Release.

1. Verify the release at `https://github.com/asasine/apt-transport-github/releases`

Artifact provenance can be verified with:

```bash
gh attestation verify ./apt-transport-github_<VERSION>_amd64.deb --owner asasine
```
9 changes: 5 additions & 4 deletions src/github.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,10 +72,11 @@ pub fn fetch_control_bytes(asset: &Asset) -> Result<Vec<u8>, Error> {
if needs_more_bytes(&buf) {
// Parse ar headers to find where control.tar.* ends
if let Some(needed) = control_tar_end_offset(&buf)
&& needed > buf.len() {
let mut rest = fetch_range(url, buf.len(), needed - 1)?;
buf.append(&mut rest);
}
&& needed > buf.len()
{
let mut rest = fetch_range(url, buf.len(), needed - 1)?;
buf.append(&mut rest);
}
}

Ok(buf)
Expand Down
Loading