docs: Improved SSH connection instructions for Windows users #36
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Release | |
| on: | |
| push: | |
| tags: | |
| - 'v*' | |
| workflow_dispatch: | |
| inputs: | |
| version: | |
| description: 'Version to release (e.g., 1.0.0)' | |
| required: true | |
| env: | |
| VERSION: ${{ github.ref_name == '' && github.event.inputs.version || github.ref_name }} | |
| GO_VERSION: '1.21' | |
| # Skip variables - set to 'true' to skip specific deployments | |
| SKIP_SNAP: ${{ vars.SKIP_SNAP || '' }} | |
| SKIP_FLATPAK: ${{ vars.SKIP_FLATPAK || '' }} | |
| SKIP_CHOCOLATEY: ${{ vars.SKIP_CHOCOLATEY || '' }} | |
| SKIP_HOMEBREW: ${{ vars.SKIP_HOMEBREW || '' }} | |
| SKIP_AUR: ${{ vars.SKIP_AUR || '' }} | |
| SKIP_PACKAGES: ${{ vars.SKIP_PACKAGES || '' }} | |
| # Require tests to pass before release | |
| jobs: | |
| test: | |
| name: Run Tests | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Set up Go | |
| uses: actions/setup-go@v5 | |
| with: | |
| go-version: ${{ env.GO_VERSION }} | |
| - name: Run tests | |
| run: go test -v -race ./... | |
| build: | |
| name: Build Binaries | |
| needs: test | |
| runs-on: ${{ matrix.os }} | |
| strategy: | |
| matrix: | |
| include: | |
| - os: ubuntu-latest | |
| goos: linux | |
| goarch: amd64 | |
| binary_name: shelldock-linux-amd64 | |
| - os: ubuntu-latest | |
| goos: linux | |
| goarch: arm64 | |
| binary_name: shelldock-linux-arm64 | |
| - os: macos-latest | |
| goos: darwin | |
| goarch: amd64 | |
| binary_name: shelldock-darwin-amd64 | |
| - os: macos-latest | |
| goos: darwin | |
| goarch: arm64 | |
| binary_name: shelldock-darwin-arm64 | |
| - os: windows-latest | |
| goos: windows | |
| goarch: amd64 | |
| binary_name: shelldock-windows-amd64.exe | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| - name: Set up Go | |
| uses: actions/setup-go@v5 | |
| with: | |
| go-version: ${{ env.GO_VERSION }} | |
| - name: Extract version (Linux/macOS) | |
| if: matrix.os != 'windows-latest' | |
| id: version | |
| shell: bash | |
| run: | | |
| if [ "${{ github.ref_type }}" = "tag" ]; then | |
| VERSION="${{ github.ref_name }}" | |
| else | |
| VERSION="v${{ github.event.inputs.version }}" | |
| fi | |
| echo "version=${VERSION#v}" >> $GITHUB_OUTPUT | |
| echo "VERSION=${VERSION}" >> $GITHUB_ENV | |
| - name: Extract version (Windows) | |
| if: matrix.os == 'windows-latest' | |
| id: version-windows | |
| shell: pwsh | |
| run: | | |
| if ("${{ github.ref_type }}" -eq "tag") { | |
| $VERSION = "${{ github.ref_name }}" | |
| } else { | |
| $VERSION = "v${{ github.event.inputs.version }}" | |
| } | |
| $VERSION_NUM = $VERSION -replace '^v', '' | |
| echo "version=$VERSION_NUM" >> $env:GITHUB_OUTPUT | |
| echo "VERSION=$VERSION" >> $env:GITHUB_ENV | |
| - name: Build binary (Linux/macOS) | |
| if: matrix.os != 'windows-latest' | |
| env: | |
| GOOS: ${{ matrix.goos }} | |
| GOARCH: ${{ matrix.goarch }} | |
| run: | | |
| VERSION_NUM="${VERSION#v}" | |
| go build -ldflags "-X main.version=${VERSION_NUM}" -o ${{ matrix.binary_name }} . | |
| - name: Install goversioninfo (Windows) | |
| if: matrix.os == 'windows-latest' | |
| shell: pwsh | |
| run: go install github.com/josephspurrier/goversioninfo/cmd/goversioninfo@latest | |
| - name: Build binary (Windows) | |
| if: matrix.os == 'windows-latest' | |
| env: | |
| GOOS: ${{ matrix.goos }} | |
| GOARCH: ${{ matrix.goarch }} | |
| shell: pwsh | |
| run: | | |
| .\scripts\build-windows.ps1 -Version "${{ steps.version-windows.outputs.version }}" -OutputName "${{ matrix.binary_name }}" | |
| - name: Upload artifact | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: ${{ matrix.binary_name }} | |
| path: ${{ matrix.binary_name }} | |
| packages: | |
| name: Build Packages | |
| needs: [test, build] | |
| runs-on: ubuntu-latest | |
| if: ${{ vars.SKIP_PACKAGES != 'true' && (needs.test.result == 'success' && needs.build.result == 'success') }} | |
| strategy: | |
| matrix: | |
| include: | |
| - distro: debian | |
| package_type: deb | |
| - distro: fedora | |
| package_type: rpm | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Set up Go | |
| uses: actions/setup-go@v5 | |
| with: | |
| go-version: ${{ env.GO_VERSION }} | |
| - name: Extract version | |
| id: version | |
| run: | | |
| if [ "${{ github.ref_type }}" = "tag" ]; then | |
| VERSION="${{ github.ref_name }}" | |
| else | |
| VERSION="v${{ github.event.inputs.version }}" | |
| fi | |
| echo "version=${VERSION#v}" >> $GITHUB_OUTPUT | |
| echo "VERSION=${VERSION}" >> $GITHUB_ENV | |
| - name: Download Linux binary | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: shelldock-linux-amd64 | |
| path: ./build/ | |
| - name: Make binary executable | |
| run: chmod +x ./build/shelldock-linux-amd64 | |
| - name: Build DEB package | |
| if: matrix.package_type == 'deb' | |
| run: | | |
| mkdir -p dist/deb/DEBIAN | |
| mkdir -p dist/deb/usr/local/bin | |
| mkdir -p dist/deb/usr/share/doc/shelldock | |
| mkdir -p dist/deb/usr/share/shelldock/repository | |
| cp ./build/shelldock-linux-amd64 dist/deb/usr/local/bin/shelldock | |
| cp README.md dist/deb/usr/share/doc/shelldock/ | |
| # Copy repository with all subdirectories | |
| cp -r repository/. dist/deb/usr/share/shelldock/repository/ 2>/dev/null || true | |
| sed "s/VERSION/${{ steps.version.outputs.version }}/g" packaging/deb/control > dist/deb/DEBIAN/control | |
| cp packaging/deb/postinst dist/deb/DEBIAN/postinst | |
| chmod +x dist/deb/DEBIAN/postinst | |
| dpkg-deb --build dist/deb dist/shelldock_${{ steps.version.outputs.version }}_amd64.deb | |
| - name: Build RPM package | |
| if: matrix.package_type == 'rpm' | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y rpm | |
| mkdir -p dist/rpm/BUILD dist/rpm/BUILDROOT dist/rpm/RPMS dist/rpm/SOURCES dist/rpm/SPECS | |
| # Create source tarball structure | |
| VERSION="${{ steps.version.outputs.version }}" | |
| mkdir -p dist/rpm/tmp/shelldock-${VERSION} | |
| cp ./build/shelldock-linux-amd64 dist/rpm/tmp/shelldock-${VERSION}/shelldock | |
| cp README.md dist/rpm/tmp/shelldock-${VERSION}/ | |
| # Copy repository with all subdirectories | |
| cp -r repository/. dist/rpm/tmp/shelldock-${VERSION}/repository/ 2>/dev/null || true | |
| # Create tarball | |
| cd dist/rpm/tmp | |
| tar czf ../SOURCES/shelldock-${VERSION}.tar.gz shelldock-${VERSION} | |
| cd ../../.. | |
| # Create spec file with proper date and architecture | |
| CHANGELOG_DATE=$(date +"%a %b %d %Y") | |
| sed -e "s/VERSION/${VERSION}/g" -e "s/Wed Jan 01 2024/${CHANGELOG_DATE}/g" -e "s/BuildArch: noarch/BuildArch: x86_64/g" packaging/rpm/shelldock.spec > dist/rpm/SPECS/shelldock.spec | |
| rpmbuild --define "_topdir $(pwd)/dist/rpm" -bb dist/rpm/SPECS/shelldock.spec | |
| - name: Upload package | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: ${{ matrix.package_type }}-package | |
| path: | | |
| dist/*.${{ matrix.package_type }} | |
| dist/rpm/RPMS/*/*.rpm | |
| apt-repo: | |
| name: Create APT Repository | |
| needs: [test, build, packages] | |
| runs-on: ubuntu-latest | |
| if: ${{ vars.SKIP_PACKAGES != 'true' && (needs.test.result == 'success' && needs.build.result == 'success') }} | |
| permissions: | |
| contents: write | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Extract version | |
| id: version | |
| run: | | |
| if [ "${{ github.ref_type }}" = "tag" ]; then | |
| VERSION="${{ github.ref_name }}" | |
| else | |
| VERSION="v${{ github.event.inputs.version }}" | |
| fi | |
| echo "version=${VERSION#v}" >> $GITHUB_OUTPUT | |
| - name: Download DEB packages | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: deb-package | |
| path: ./apt-repo/pool/main/s/shelldock | |
| - name: Setup APT repository tools | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y dpkg-dev apt-utils gnupg2 | |
| - name: Import GPG key | |
| env: | |
| GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }} | |
| GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} | |
| run: | | |
| if [ -z "$GPG_PRIVATE_KEY" ]; then | |
| echo "GPG_PRIVATE_KEY not set, skipping GPG signing" | |
| exit 0 | |
| fi | |
| echo "$GPG_PRIVATE_KEY" | gpg --batch --yes --pinentry-mode loopback --passphrase "$GPG_PASSPHRASE" --import | |
| gpg --list-secret-keys --keyid-format LONG | |
| continue-on-error: true | |
| - name: Create APT repository structure | |
| run: | | |
| mkdir -p apt-repo/dists/stable/main/binary-amd64 | |
| mkdir -p apt-repo/dists/stable/main/binary-arm64 | |
| # Move .deb files to pool | |
| find apt-repo/pool -name "*.deb" -exec mv {} apt-repo/pool/main/s/shelldock/ \; | |
| # Generate Packages file for amd64 | |
| cd apt-repo | |
| dpkg-scanpackages --arch amd64 pool/main > dists/stable/main/binary-amd64/Packages 2>/dev/null || true | |
| gzip -k dists/stable/main/binary-amd64/Packages | |
| # Generate Packages file for arm64 (if available) | |
| dpkg-scanpackages --arch arm64 pool/main > dists/stable/main/binary-arm64/Packages 2>/dev/null || true | |
| gzip -k dists/stable/main/binary-arm64/Packages 2>/dev/null || true | |
| - name: Generate Release file | |
| env: | |
| GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }} | |
| GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} | |
| run: | | |
| if [ -z "$GPG_PRIVATE_KEY" ]; then | |
| echo "GPG_PRIVATE_KEY not set, creating unsigned Release" | |
| cd apt-repo/dists/stable | |
| cat > Release << EOF | |
| Origin: ShellDock | |
| Label: ShellDock | |
| Suite: stable | |
| Codename: stable | |
| Version: ${{ steps.version.outputs.version }} | |
| Architectures: amd64 arm64 | |
| Components: main | |
| Description: ShellDock APT Repository | |
| EOF | |
| exit 0 | |
| fi | |
| cd apt-repo/dists/stable | |
| cat > Release << EOF | |
| Origin: ShellDock | |
| Label: ShellDock | |
| Suite: stable | |
| Codename: stable | |
| Version: ${{ steps.version.outputs.version }} | |
| Architectures: amd64 arm64 | |
| Components: main | |
| Description: ShellDock APT Repository | |
| EOF | |
| # Add file hashes | |
| find main -type f | while read file; do | |
| echo " $(md5sum "$file" | cut -d' ' -f1) $(stat -c%s "$file") $file" >> Release | |
| echo " $(sha256sum "$file" | cut -d' ' -f1) $(stat -c%s "$file") $file" >> Release | |
| done | |
| - name: Sign Release file | |
| env: | |
| GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }} | |
| GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} | |
| run: | | |
| if [ -z "$GPG_PRIVATE_KEY" ]; then | |
| echo "GPG_PRIVATE_KEY not set, skipping signing" | |
| exit 0 | |
| fi | |
| cd apt-repo/dists/stable | |
| echo "$GPG_PASSPHRASE" | gpg --batch --yes --pinentry-mode loopback --passphrase-fd 0 --clearsign -o InRelease Release | |
| echo "$GPG_PASSPHRASE" | gpg --batch --yes --pinentry-mode loopback --passphrase-fd 0 --detach-sign -o Release.gpg Release | |
| continue-on-error: true | |
| - name: Export GPG public key | |
| env: | |
| GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }} | |
| GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} | |
| run: | | |
| if [ -z "$GPG_PRIVATE_KEY" ]; then | |
| echo "GPG_PRIVATE_KEY not set, skipping key export" | |
| exit 0 | |
| fi | |
| mkdir -p apt-repo | |
| gpg --batch --yes --pinentry-mode loopback --passphrase "$GPG_PASSPHRASE" --export --armor > apt-repo/gpg.key || true | |
| continue-on-error: true | |
| - name: Upload APT repository | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: apt-repository | |
| path: apt-repo/ | |
| snap: | |
| name: Build and Publish Snap | |
| needs: [test, build] | |
| runs-on: ubuntu-latest | |
| if: ${{ vars.SKIP_SNAP != 'true' && (needs.test.result == 'success' && needs.build.result == 'success') }} | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Extract version | |
| id: version | |
| run: | | |
| if [ "${{ github.ref_type }}" = "tag" ]; then | |
| VERSION="${{ github.ref_name }}" | |
| else | |
| VERSION="v${{ github.event.inputs.version }}" | |
| fi | |
| echo "version=${VERSION#v}" >> $GITHUB_OUTPUT | |
| - name: Download Linux binary | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: shelldock-linux-amd64 | |
| path: ./ | |
| - name: Make binary executable | |
| run: chmod +x shelldock-linux-amd64 | |
| - name: Setup Snapcraft | |
| run: | | |
| sudo snap install snapcraft --classic | |
| - name: Create snapcraft.yaml | |
| run: | | |
| mkdir -p snap | |
| sed "s/VERSION_PLACEHOLDER/${{ steps.version.outputs.version }}/g" \ | |
| packaging/snap/snapcraft.yaml.template > snap/snapcraft.yaml | |
| echo "Generated snapcraft.yaml:" | |
| cat snap/snapcraft.yaml | |
| # Verify repository directory exists for snap | |
| if [ ! -d "repository" ]; then | |
| echo "⚠️ Warning: repository directory not found" | |
| else | |
| echo "✅ Repository directory found, will be included in snap" | |
| fi | |
| - name: Build snap | |
| run: | | |
| sudo snap install core22 | |
| snapcraft --destructive-mode | |
| - name: Publish to Snap Store | |
| env: | |
| SNAPCRAFT_STORE_CREDENTIALS: ${{ secrets.SNAP_STORE_CREDENTIALS }} | |
| run: | | |
| chmod +x scripts/publish-snap.sh | |
| ./scripts/publish-snap.sh | |
| continue-on-error: true | |
| - name: Upload snap artifact | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: snap-package | |
| path: "*.snap" | |
| flatpak: | |
| name: Build Flatpak Package | |
| needs: [test, build] | |
| runs-on: ubuntu-latest | |
| if: ${{ vars.SKIP_FLATPAK != 'true' && (needs.test.result == 'success' && needs.build.result == 'success') }} | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Extract version | |
| id: version | |
| run: | | |
| if [ "${{ github.ref_type }}" = "tag" ]; then | |
| VERSION="${{ github.ref_name }}" | |
| else | |
| VERSION="v${{ github.event.inputs.version }}" | |
| fi | |
| echo "version=${VERSION#v}" >> $GITHUB_OUTPUT | |
| - name: Download Linux binary | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: shelldock-linux-amd64 | |
| path: ./ | |
| - name: Setup Flatpak | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y flatpak flatpak-builder | |
| sudo flatpak remote-add --if-not-exists --system flathub https://flathub.org/repo/flathub.flatpakrepo | |
| sudo flatpak install -y --system flathub org.freedesktop.Platform//22.08 org.freedesktop.Sdk//22.08 | |
| - name: Create flatpak manifest | |
| run: | | |
| mkdir -p flatpak | |
| cp shelldock-linux-amd64 flatpak/ | |
| # Copy repository with all subdirectories to flatpak directory | |
| # flatpak-builder looks for sources relative to the manifest file location | |
| cp -r repository flatpak/ 2>/dev/null || (echo "Warning: repository directory not found" && mkdir -p flatpak/repository) | |
| cp packaging/flatpak/com.github.opsguild.shelldock.yml flatpak/ | |
| # Verify repository was copied | |
| if [ -d "flatpak/repository" ]; then | |
| echo "✅ Repository directory copied to flatpak/" | |
| ls -la flatpak/repository/ | head -10 | |
| else | |
| echo "⚠️ Warning: Repository directory not found in flatpak/" | |
| fi | |
| - name: Build flatpak | |
| run: | | |
| flatpak-builder --force-clean build flatpak/com.github.opsguild.shelldock.yml --repo=repo | |
| flatpak build-bundle repo shelldock-${{ steps.version.outputs.version }}.flatpak com.github.opsguild.shelldock | |
| - name: Publish to Flathub (if configured) | |
| env: | |
| FLATHUB_SSH_KEY: ${{ secrets.FLATHUB_SSH_KEY }} | |
| run: | | |
| if [ -z "$FLATHUB_SSH_KEY" ]; then | |
| echo "FLATHUB_SSH_KEY not set, skipping Flathub publish" | |
| exit 0 | |
| fi | |
| # Setup SSH | |
| mkdir -p ~/.ssh | |
| echo "$FLATHUB_SSH_KEY" > ~/.ssh/flathub_key | |
| chmod 600 ~/.ssh/flathub_key | |
| ssh-keyscan github.com >> ~/.ssh/known_hosts | |
| # Clone Flathub repository | |
| git clone git@github.com:flathub/com.github.opsguild.shelldock.git flathub-repo || \ | |
| git clone https://github.com/flathub/com.github.opsguild.shelldock.git flathub-repo || \ | |
| echo "Flathub repository not found. You may need to submit to Flathub first." | |
| if [ -d flathub-repo ]; then | |
| cd flathub-repo | |
| git config user.name "github-actions[bot]" | |
| git config user.email "github-actions[bot]@users.noreply.github.com" | |
| # Update manifest with new version | |
| FLATPAK_FILE=$(ls ../*.flatpak | head -1) | |
| # Extract and update version in manifest if needed | |
| # This is a simplified version - you may need to adjust based on your Flathub manifest structure | |
| git add . | |
| git diff --staged --quiet || git commit -m "Update to ${{ github.ref_name }}" | |
| git push origin master || git push origin main || echo "Push failed" | |
| fi | |
| continue-on-error: true | |
| - name: Upload flatpak | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: flatpak-package | |
| path: "*.flatpak" | |
| homebrew: | |
| name: Update Homebrew Formula | |
| needs: [test, build] | |
| runs-on: ubuntu-latest | |
| if: ${{ vars.SKIP_HOMEBREW != 'true' && (needs.test.result == 'success' && needs.build.result == 'success') && github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v') }} | |
| permissions: | |
| contents: write | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| token: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Extract version | |
| id: version | |
| run: | | |
| VERSION="${{ github.ref_name }}" | |
| echo "version=${VERSION#v}" >> $GITHUB_OUTPUT | |
| - name: Download macOS binaries | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: shelldock-darwin-amd64 | |
| path: ./build/ | |
| continue-on-error: true | |
| - name: Download macOS ARM64 binary | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: shelldock-darwin-arm64 | |
| path: ./build/ | |
| continue-on-error: true | |
| - name: Calculate SHA256 for amd64 | |
| id: sha256_amd64 | |
| run: | | |
| if [ -f build/shelldock-darwin-amd64 ]; then | |
| SHA256=$(sha256sum build/shelldock-darwin-amd64 | cut -d' ' -f1) | |
| echo "sha256=$SHA256" >> $GITHUB_OUTPUT | |
| fi | |
| - name: Calculate SHA256 for arm64 | |
| id: sha256_arm64 | |
| run: | | |
| if [ -f build/shelldock-darwin-arm64 ]; then | |
| SHA256=$(sha256sum build/shelldock-darwin-arm64 | cut -d' ' -f1) | |
| echo "sha256=$SHA256" >> $GITHUB_OUTPUT | |
| fi | |
| - name: Create Homebrew formula | |
| run: | | |
| mkdir -p Formula | |
| cat > Formula/shelldock.rb << EOF | |
| class Shelldock < Formula | |
| desc "A fast, cross-platform shell command repository manager" | |
| homepage "https://github.com/OpsGuild/ShellDock" | |
| url "https://github.com/OpsGuild/ShellDock/archive/${{ github.ref_name }}.tar.gz" | |
| version "${{ steps.version.outputs.version }}" | |
| sha256 "${{ steps.sha256_amd64.outputs.sha256 }}" | |
| on_macos do | |
| if Hardware::CPU.intel? | |
| url "https://github.com/OpsGuild/ShellDock/releases/download/${{ github.ref_name }}/shelldock-darwin-amd64" | |
| sha256 "${{ steps.sha256_amd64.outputs.sha256 }}" | |
| else | |
| url "https://github.com/OpsGuild/ShellDock/releases/download/${{ github.ref_name }}/shelldock-darwin-arm64" | |
| sha256 "${{ steps.sha256_arm64.outputs.sha256 }}" | |
| end | |
| end | |
| def install | |
| if OS.mac? | |
| bin.install "shelldock-darwin-#{Hardware::CPU.arch == "arm64" ? "arm64" : "amd64"}" => "shelldock" | |
| end | |
| end | |
| test do | |
| system "#{bin}/shelldock", "--version" | |
| end | |
| end | |
| EOF | |
| - name: Push to Homebrew tap | |
| env: | |
| HOMEBREW_TAP_TOKEN: ${{ secrets.HOMEBREW_TAP_TOKEN }} | |
| run: | | |
| set +x # Prevent token from being logged | |
| if [ -z "$HOMEBREW_TAP_TOKEN" ]; then | |
| echo "HOMEBREW_TAP_TOKEN not set, skipping tap push" | |
| echo "To enable Homebrew tap, create https://github.com/OpsGuild/homebrew-tap and add HOMEBREW_TAP_TOKEN" | |
| exit 0 | |
| fi | |
| # Verify token format (fine-grained tokens start with github_pat_) | |
| if [[ "$HOMEBREW_TAP_TOKEN" != github_pat_* ]]; then | |
| echo "⚠️ Warning: Token doesn't appear to be a fine-grained token (should start with 'github_pat_')" | |
| echo "If using a classic token, ensure it has 'repo' scope" | |
| fi | |
| # Clone tap repository with token | |
| echo "Cloning homebrew-tap repository..." | |
| if ! git clone https://${HOMEBREW_TAP_TOKEN}@github.com/OpsGuild/homebrew-tap.git homebrew-tap; then | |
| echo "❌ Failed to clone homebrew-tap repository" | |
| echo "Verify:" | |
| echo " 1. Repository exists at https://github.com/OpsGuild/homebrew-tap" | |
| echo " 2. Token has 'Contents: Read and write' permission for the repository" | |
| echo " 3. If organization has SSO, token must be authorized for SSO" | |
| exit 1 | |
| fi | |
| cd homebrew-tap | |
| # Determine the default branch | |
| DEFAULT_BRANCH=$(git remote show origin | grep "HEAD branch" | awk '{print $3}' || echo "main") | |
| echo "Using branch: $DEFAULT_BRANCH" | |
| # Checkout the default branch | |
| git checkout $DEFAULT_BRANCH 2>/dev/null || git checkout -b $DEFAULT_BRANCH | |
| # Update remote URL to include token for push operations | |
| git remote set-url origin https://${HOMEBREW_TAP_TOKEN}@github.com/OpsGuild/homebrew-tap.git | |
| # Copy formula | |
| mkdir -p Formula | |
| cp ../Formula/shelldock.rb Formula/ | |
| # Commit and push | |
| git config user.name "github-actions[bot]" | |
| git config user.email "github-actions[bot]@users.noreply.github.com" | |
| git add Formula/shelldock.rb | |
| if ! git diff --staged --quiet; then | |
| git commit -m "Update shelldock to ${{ github.ref_name }}" | |
| echo "Pushing to homebrew-tap..." | |
| # Push with token explicitly in URL | |
| if ! git push https://${HOMEBREW_TAP_TOKEN}@github.com/OpsGuild/homebrew-tap.git refs/heads/$DEFAULT_BRANCH:refs/heads/$DEFAULT_BRANCH; then | |
| echo "❌ Failed to push to homebrew-tap" | |
| echo "Verify:" | |
| echo " 1. Token has 'Contents: Read and write' permission" | |
| echo " 2. Token is authorized for SSO (if organization uses SSO)" | |
| echo " 3. Repository exists and is accessible" | |
| exit 1 | |
| fi | |
| echo "✅ Successfully pushed to homebrew-tap" | |
| else | |
| echo "No changes to commit" | |
| fi | |
| continue-on-error: true | |
| chocolatey: | |
| name: Build and Publish Chocolatey Package | |
| needs: [test, build] | |
| runs-on: windows-latest | |
| if: ${{ vars.SKIP_CHOCOLATEY != 'true' && (needs.test.result == 'success' && needs.build.result == 'success') }} | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Extract version | |
| id: version | |
| run: | | |
| if ("${{ github.ref_type }}" -eq "tag") { | |
| $VERSION = "${{ github.ref_name }}" | |
| } else { | |
| $VERSION = "v${{ github.event.inputs.version }}" | |
| } | |
| # Remove 'v' prefix if present | |
| $VERSION_NUM = $VERSION -replace '^v', '' | |
| Write-Host "Extracted version: $VERSION_NUM from $VERSION" | |
| echo "version=$VERSION_NUM" >> $env:GITHUB_OUTPUT | |
| - name: Download Windows binary | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: shelldock-windows-amd64.exe | |
| path: ./ | |
| - name: Create Chocolatey package | |
| shell: pwsh | |
| run: | | |
| mkdir -p chocolatey\shelldock\tools | |
| Move-Item shelldock-windows-amd64.exe chocolatey\shelldock\tools\shelldock.exe | |
| # Copy repository with all subdirectories | |
| if (Test-Path "repository") { | |
| New-Item -ItemType Directory -Path "chocolatey\shelldock\tools\repository" -Force | Out-Null | |
| Copy-Item -Path "repository\*" -Destination "chocolatey\shelldock\tools\repository\" -Recurse -Force | |
| Write-Host "✅ Repository files copied to Chocolatey package" | |
| } else { | |
| Write-Warning "Repository directory not found" | |
| } | |
| # Copy icon if it exists | |
| if (Test-Path "static\images\icon.jpg") { | |
| Copy-Item "static\images\icon.jpg" "chocolatey\shelldock\tools\icon.jpg" | |
| } | |
| # Copy LICENSE.txt from root LICENSE file | |
| if (Test-Path "LICENSE") { | |
| Copy-Item "LICENSE" "chocolatey\shelldock\tools\LICENSE.txt" | |
| } else { | |
| Write-Warning "LICENSE file not found, creating default MIT license" | |
| "MIT License`n`nCopyright (c) 2024 OpsGuild" | Out-File -Encoding UTF8 chocolatey\shelldock\tools\LICENSE.txt | |
| } | |
| # Copy VERIFICATION.txt from packaging directory | |
| if (Test-Path "packaging\chocolatey\VERIFICATION.txt") { | |
| Copy-Item "packaging\chocolatey\VERIFICATION.txt" "chocolatey\shelldock\tools\VERIFICATION.txt" | |
| } else { | |
| Write-Warning "VERIFICATION.txt not found, creating default" | |
| "VERIFICATION`n`nThe software is open source and available at:`nhttps://github.com/OpsGuild/ShellDock" | Out-File -Encoding UTF8 chocolatey\shelldock\tools\VERIFICATION.txt | |
| } | |
| # Copy chocolateyUninstall.ps1 from packaging directory | |
| if (Test-Path "packaging\chocolatey\chocolateyUninstall.ps1") { | |
| Copy-Item "packaging\chocolatey\chocolateyUninstall.ps1" "chocolatey\shelldock\tools\chocolateyUninstall.ps1" | |
| } else { | |
| Write-Warning "chocolateyUninstall.ps1 not found" | |
| } | |
| # Copy chocolateyInstall.ps1 from packaging directory | |
| if (Test-Path "packaging\chocolatey\chocolateyInstall.ps1") { | |
| Copy-Item "packaging\chocolatey\chocolateyInstall.ps1" "chocolatey\shelldock\tools\chocolateyinstall.ps1" | |
| } else { | |
| Write-Warning "chocolateyInstall.ps1 not found" | |
| } | |
| # Create nuspec with dynamic version from template | |
| $VERSION = "${{ steps.version.outputs.version }}" | |
| $nuspec = Get-Content "packaging\chocolatey\shelldock.nuspec.template" -Raw -ErrorAction SilentlyContinue | |
| if (-not $nuspec) { | |
| Write-Error "nuspec template not found at packaging\chocolatey\shelldock.nuspec.template" | |
| exit 1 | |
| } | |
| $nuspec = $nuspec -replace 'VERSION_PLACEHOLDER', $VERSION | |
| $nuspec | Out-File -Encoding UTF8 chocolatey\shelldock\shelldock.nuspec | |
| - name: Install Chocolatey | |
| run: | | |
| Set-ExecutionPolicy Bypass -Scope Process -Force | |
| [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072 | |
| iex ((New-Object System.Net.WebClient).DownloadString('https://community.chocolatey.org/install.ps1')) | |
| - name: Build Chocolatey package | |
| run: | | |
| choco pack chocolatey\shelldock\shelldock.nuspec --output-directory chocolatey | |
| - name: Set Chocolatey API key | |
| env: | |
| CHOCOLATEY_API_KEY: ${{ secrets.CHOCOLATEY_API_KEY }} | |
| shell: pwsh | |
| run: | | |
| if ([string]::IsNullOrEmpty($env:CHOCOLATEY_API_KEY)) { | |
| Write-Host "CHOCOLATEY_API_KEY not set, skipping API key setup" | |
| exit 0 | |
| } | |
| choco apikey --key "$env:CHOCOLATEY_API_KEY" --source https://push.chocolatey.org/ | |
| - name: Publish to Chocolatey | |
| env: | |
| CHOCOLATEY_API_KEY: ${{ secrets.CHOCOLATEY_API_KEY }} | |
| shell: pwsh | |
| run: | | |
| if ([string]::IsNullOrEmpty($env:CHOCOLATEY_API_KEY)) { | |
| Write-Host "CHOCOLATEY_API_KEY not set, skipping publish" | |
| exit 0 | |
| } | |
| $package = Get-ChildItem chocolatey\*.nupkg | Select-Object -First 1 | |
| choco push $package.FullName --source https://push.chocolatey.org/ --force | |
| continue-on-error: true | |
| - name: Upload Chocolatey package | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: chocolatey-package | |
| path: "chocolatey/*.nupkg" | |
| aur: | |
| name: Update AUR Package | |
| needs: [test, build] | |
| runs-on: ubuntu-latest | |
| if: ${{ vars.SKIP_AUR != 'true' && (needs.test.result == 'success' && needs.build.result == 'success') && github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v') }} | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Extract version | |
| id: version | |
| run: | | |
| VERSION="${{ github.ref_name }}" | |
| echo "version=${VERSION#v}" >> $GITHUB_OUTPUT | |
| - name: Download Linux binary | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: shelldock-linux-amd64 | |
| path: ./ | |
| - name: Calculate SHA256 | |
| id: sha256 | |
| run: | | |
| SHA256=$(sha256sum shelldock-linux-amd64 | cut -d' ' -f1) | |
| echo "sha256=$SHA256" >> $GITHUB_OUTPUT | |
| - name: Update PKGBUILD | |
| run: | | |
| # Generate PKGBUILD from template | |
| sed -e "s/VERSION_PLACEHOLDER/${{ steps.version.outputs.version }}/g" \ | |
| -e "s/SHA256_PLACEHOLDER/${{ steps.sha256.outputs.sha256 }}/g" \ | |
| packaging/arch/PKGBUILD.template > packaging/arch/PKGBUILD | |
| echo "Generated PKGBUILD:" | |
| cat packaging/arch/PKGBUILD | |
| - name: Generate .SRCINFO | |
| run: | | |
| chmod +x scripts/generate-srcinfo.sh | |
| ./scripts/generate-srcinfo.sh "${{ steps.version.outputs.version }}" "${{ steps.sha256.outputs.sha256 }}" | |
| - name: Publish to AUR | |
| env: | |
| AUR_SSH_KEY: ${{ secrets.AUR_SSH_KEY }} | |
| run: | | |
| chmod +x scripts/publish-aur.sh | |
| ./scripts/publish-aur.sh "${{ steps.version.outputs.version }}" | |
| continue-on-error: true | |
| - name: Upload PKGBUILD | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: aur-package | |
| path: packaging/arch/PKGBUILD | |
| gpg-sign: | |
| name: GPG Sign Packages | |
| needs: [build, packages, snap, flatpak, chocolatey] | |
| runs-on: ubuntu-latest | |
| if: always() && needs.build.result == 'success' | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Import GPG key | |
| env: | |
| GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }} | |
| GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} | |
| run: | | |
| if [ -z "$GPG_PRIVATE_KEY" ]; then | |
| echo "GPG_PRIVATE_KEY not set, skipping GPG signing" | |
| exit 0 | |
| fi | |
| echo "$GPG_PRIVATE_KEY" | gpg --batch --yes --pinentry-mode loopback --passphrase "$GPG_PASSPHRASE" --import | |
| gpg --list-secret-keys --keyid-format LONG | |
| continue-on-error: true | |
| - name: Download all artifacts | |
| uses: actions/download-artifact@v4 | |
| with: | |
| path: ./artifacts | |
| - name: Sign binaries | |
| env: | |
| GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }} | |
| GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} | |
| run: | | |
| if [ -z "$GPG_PRIVATE_KEY" ]; then | |
| echo "GPG_PRIVATE_KEY not set, skipping signing" | |
| exit 0 | |
| fi | |
| for file in $(find artifacts -type f \( -name "shelldock-*" -o -name "*.deb" -o -name "*.rpm" -o -name "*.snap" -o -name "*.flatpak" -o -name "*.nupkg" \)); do | |
| echo "$GPG_PASSPHRASE" | gpg --batch --yes --pinentry-mode loopback --passphrase-fd 0 --detach-sign --armor "$file" | |
| done | |
| continue-on-error: true | |
| - name: Upload signatures | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: gpg-signatures | |
| path: "artifacts/*.asc" | |
| release: | |
| name: Create Release | |
| needs: [test, build, packages, snap, flatpak, chocolatey, homebrew, aur, gpg-sign, apt-repo] | |
| runs-on: ubuntu-latest | |
| if: always() && needs.test.result == 'success' && needs.build.result == 'success' | |
| permissions: | |
| contents: write | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Extract version | |
| id: version | |
| run: | | |
| if [ "${{ github.ref_type }}" = "tag" ]; then | |
| VERSION="${{ github.ref_name }}" | |
| else | |
| VERSION="v${{ github.event.inputs.version }}" | |
| fi | |
| echo "version=${VERSION#v}" >> $GITHUB_OUTPUT | |
| echo "VERSION=${VERSION}" >> $GITHUB_ENV | |
| - name: Download all artifacts | |
| uses: actions/download-artifact@v4 | |
| with: | |
| path: ./release-assets | |
| - name: Prepare release files | |
| run: | | |
| # Create a clean release directory | |
| mkdir -p release-files | |
| # Copy unique files (avoid duplicates) | |
| find release-assets -name "*.deb" -type f | head -1 | xargs -I {} cp {} release-files/ 2>/dev/null || true | |
| find release-assets -name "*.rpm" -type f | head -1 | xargs -I {} cp {} release-files/ 2>/dev/null || true | |
| find release-assets -name "*.snap" -type f | head -1 | xargs -I {} cp {} release-files/ 2>/dev/null || true | |
| find release-assets -name "*.flatpak" -type f | head -1 | xargs -I {} cp {} release-files/ 2>/dev/null || true | |
| find release-assets -name "*.nupkg" -type f | head -1 | xargs -I {} cp {} release-files/ 2>/dev/null || true | |
| find release-assets -name "*.exe" -type f | head -1 | xargs -I {} cp {} release-files/ 2>/dev/null || true | |
| find release-assets -name "PKGBUILD" -type f | head -1 | xargs -I {} cp {} release-files/ 2>/dev/null || true | |
| # Copy binaries (shelldock-* but not .deb/.rpm/etc) | |
| find release-assets -name "shelldock-*" -type f ! -name "*.deb" ! -name "*.rpm" ! -name "*.snap" ! -name "*.flatpak" ! -name "*.nupkg" ! -name "*.exe" -exec cp {} release-files/ \; 2>/dev/null || true | |
| # Extract GPG key if it exists | |
| if [ -f release-assets/apt-repository/apt-repo/gpg.key ]; then | |
| cp release-assets/apt-repository/apt-repo/gpg.key release-files/gpg.key | |
| fi | |
| # List files to be uploaded | |
| echo "Files to upload:" | |
| ls -lh release-files/ || true | |
| - name: Create Release | |
| uses: softprops/action-gh-release@v1 | |
| with: | |
| files: release-files/* | |
| draft: false | |
| prerelease: false | |
| generate_release_notes: true | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} |