diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index b0b236f..2f508d0 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -44,36 +44,45 @@ jobs: strategy: fail-fast: false matrix: - build: ['linux', 'macos', 'arm-macos', 'windows'] + build: ['linux', 'debian', 'macos', 'arm-macos', 'windows'] include: - build: linux os: ubuntu-22.04 target: x86_64-unknown-linux-musl + packages: apt + + - build: debian + os: ubuntu-22.04 + target: x86_64-unknown-linux-musl + packages: apt - build: macos os: macos-latest target: x86_64-apple-darwin + packages: brew - build: arm-macos os: macos-latest target: aarch64-apple-darwin + packages: brew - build: windows os: windows-2022 target: x86_64-pc-windows-msvc + packages: none steps: - name: Checkout code uses: actions/checkout@v4 - name: Install packages (linux) - if: matrix.build == 'linux' + if: matrix.packages == 'apt' run: | sudo apt-get update sudo apt-get install -y pandoc - name: Install packages (macos) - if: matrix.build == 'macos' || matrix.build == 'arm-macos' + if: matrix.packages == 'brew' run: | brew update brew install pandoc diff --git a/Cargo.toml b/Cargo.toml index c4c05c8..7e9f5ac 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,12 +1,14 @@ [package] name = "fwd" version = "0.9.2" +authors = ["John Doty "] edition = "2021" license = "MIT" -description = "Automatically forward ports to a remote server over ssh" +description = "Automatically forward ports to a remote server" +readme = "README.md" +documentation = "https://github.com/DeCarabas/fwd" homepage = "https://github.com/DeCarabas/fwd" repository = "https://github.com/DeCarabas/fwd" -readme = "README.md" [[bin]] name = "fwd-browse" @@ -41,3 +43,21 @@ procfs = "0.14.1" [target.'cfg(target_family="unix")'.dependencies] libc = "0.2.155" + +[package.metadata.deb] +section = "net" +depends = [] # No auto deps? +assets = [ + ["target/release/fwd", "usr/bin/", "755"], + ["LICENSE", "usr/share/doc/fwd/", "644"], + ["README.md", "usr/share/doc/fwd/README", "644"], + # The man page is automatically generated by fwd's build process. See + # release.py for details. + ["target/release/fwd.1", "usr/share/man/man1/fwd.1", "644"], +] +extended-description = """\ +fwd enumerates the listening ports the remote server and automatically listens +for connections on the same ports on the local machine. When fwd receives a +connection on the local machine, it automatically forwards that connection to +the remote machine. +""" diff --git a/LICENSE b/LICENSE index 7857f35..630f272 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright 2022 John Doty +Copyright 2024 John Doty Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index 1e3c07d..e0ac026 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ A port-forwarding utility. Here's how it works: 1. Get the latest [release](https://github.com/DeCarabas/fwd/releases) of `fwd` -2. You install `fwd` on the server somewhere in your `$PATH` (like `/usr/bin/`) +2. You install `fwd` on the server somewhere in your `$PATH` (like `/usr/bin/`, or `.local/bin`) 3. You install `fwd` on the client (like your laptop) 4. You run `fwd` on the client to connect to the server, like so: @@ -21,9 +21,3 @@ If the port is something that might be interesting to a web browser, you can pre If something is going wrong, pressing `l` will toggle logs that might explain it. Press `q` to quit. - -## Future Improvements: - -- Clipboard integration: send something from the remote end of the pipe to the host's clipboard. (Sometimes you *really* want to copy some big buffer from the remote side and your terminal just can't make that work.) - -- Client heartbeats: I frequently wind up in a situation where the pipe is stalled: not broken but nothing is getting through. (This happens with my coder.com pipes all the time.) diff --git a/release.py b/release.py index b3c8546..381a2a7 100644 --- a/release.py +++ b/release.py @@ -5,6 +5,7 @@ little nicer for running commands, it's worse at everything else. """ import dataclasses +import enum import os import os.path import pathlib @@ -18,14 +19,20 @@ if BUILD is None: raise Exception("you *must* set the BUILD environment variable") +class Archive(enum.Enum): + TARBALL = 1 + ZIP = 2 + DEB = 3 + + @dataclasses.dataclass class BuildSettings: - target: str - test: bool = True - man_page: bool = True - strip: bool = True - windows: bool = False - ext: str = "" + target: str # The rust target to build for + test: bool = True # Whether or not to run tests + man_page: bool = True # Whether or not to generate a man page + strip: bool = True # Whether or not to strip binaries + archive: Archive = Archive.TARBALL # Archive type + ext: str = "" # The file extension of the binary print(f"doing release: {BUILD}") @@ -33,6 +40,11 @@ build = { "linux": BuildSettings( target="x86_64-unknown-linux-musl", ), + "deb": BuildSettings( + target="x86_64-unknown-linux-musl", + test=False, + archive=Archive.DEB, + ), "macos": BuildSettings( target="x86_64-apple-darwin", ), @@ -43,7 +55,7 @@ build = { target="x86_64-pc-windows-msvc", strip=False, man_page=False, - windows=True, + archive=Archive.ZIP, ext=".exe", ), }[BUILD] @@ -100,29 +112,50 @@ def build_docs(staging: pathlib.Path): f.write(contents) +def build_archive(staging: pathlib.Path) -> pathlib.Path: + print("Creating archive...") + if build.archive == Archive.ZIP: + archive = pathlib.Path(f"{staging}.zip") + subprocess.run(["7z", "a", archive, f"{staging}"], check=True) + + elif build.archive == Archive.DEB: + subprocess.run(["cargo", "install", "cargo-deb"], check=True) + shutil.copyfile(staging / "fwd.1", target_dir / "fwd.1") + subprocess.run(["cargo", "deb", "--target", build.target], check=True) + + # Knowing the deb path means knowing the target version but I don't + # actually have the version here. (Or, like, I have the release tag + # but not in testing.) So just find the hopefully singular .deb that + # we made. + deb_path = pathlib.Path("target") / build.target / "debian" + archives = list(deb_path.glob("*.deb")) + assert len(archives) == 1 + archive = archives[0] + + else: + assert build.archive == Archive.TARBALL + archive = pathlib.Path(f"{staging}.tar.gz") + subprocess.run(["tar", "czf", archive, f"{staging}"], check=True) + + return archive + + staging = pathlib.Path(f"fwd-{build.target}") os.makedirs(staging, exist_ok=True) build_and_test(staging) build_docs(staging) - -print("Creating archive...") -if build.windows: - archive = f"{staging}.zip" - subprocess.run(["7z", "a", archive, f"{staging}"], check=True) -else: - archive = f"{staging}.tar.gz" - subprocess.run(["tar", "czf", archive, f"{staging}"], check=True) +archive = build_archive(staging) shutil.rmtree(staging) +assert archive.exists() if RELEASE_TAG is None: - print("Not releasing to github, RELEASE_TAG is none.") + print(f"Not releasing {archive} to github, RELEASE_TAG is none.") else: print(f"Uploading {archive} to github release {RELEASE_TAG}...") subprocess.run( ["gh", "release", "upload", RELEASE_TAG, archive, "--clobber"], check=True, ) - -os.unlink(archive) + os.unlink(archive)