build: Add Chocolatey package files. #30
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: Build binary (Windows) | |
| if: matrix.os == 'windows-latest' | |
| env: | |
| GOOS: ${{ matrix.goos }} | |
| GOARCH: ${{ matrix.goarch }} | |
| VERSION_NUM: ${{ steps.version-windows.outputs.version }} | |
| shell: pwsh | |
| run: | | |
| go build -ldflags "-X main.version=$env:VERSION_NUM" -o ${{ 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/ | |
| cp repository/*.yaml 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}/ | |
| mkdir -p dist/rpm/tmp/shelldock-${VERSION}/repository | |
| cp repository/*.yaml 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 Snap Package | |
| 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 | |
| cat > snap/snapcraft.yaml << 'EOF' | |
| name: shelldock | |
| base: core22 | |
| version: '${{ steps.version.outputs.version }}' | |
| summary: A fast, cross-platform shell command repository manager | |
| description: | | |
| ShellDock is a fast, cross-platform tool for managing and executing | |
| saved shell commands from bundled repository or local directory. | |
| grade: stable | |
| confinement: strict | |
| title: ShellDock | |
| license: MIT | |
| contact: https://github.com/OpsGuild/ShellDock | |
| website: https://github.com/OpsGuild/ShellDock | |
| source-code: https://github.com/OpsGuild/ShellDock | |
| apps: | |
| shelldock: | |
| command: usr/bin/shelldock | |
| plugs: | |
| - home | |
| - network | |
| parts: | |
| shelldock: | |
| plugin: dump | |
| source: . | |
| organize: | |
| shelldock-linux-amd64: usr/bin/shelldock | |
| EOF | |
| - name: Build snap | |
| run: | | |
| sudo snap install core22 | |
| snapcraft --destructive-mode | |
| - name: Login to Snap Store | |
| env: | |
| SNAP_STORE_CREDENTIALS_B64: ${{ secrets.SNAP_STORE_CREDENTIALS }} | |
| run: | | |
| set +x # Prevent command echoing to avoid exposing credentials | |
| if [ -z "$SNAP_STORE_CREDENTIALS_B64" ]; then | |
| echo "SNAP_STORE_CREDENTIALS not set, skipping Snap Store login" | |
| exit 0 | |
| fi | |
| # Decode base64 credentials to temp file | |
| CREDS_FILE=$(mktemp) | |
| echo "$SNAP_STORE_CREDENTIALS_B64" | base64 -d > "$CREDS_FILE" 2>/dev/null | |
| # Unset SNAPCRAFT_STORE_CREDENTIALS before login (snapcraft doesn't allow it set during login) | |
| unset SNAPCRAFT_STORE_CREDENTIALS | |
| # Login using the credentials file directly | |
| snapcraft login --with "$CREDS_FILE" || echo "Login failed or already logged in" | |
| # Securely remove temp file | |
| shred -u "$CREDS_FILE" 2>/dev/null || rm -f "$CREDS_FILE" | |
| continue-on-error: true | |
| - name: Register snap name (if not already registered) | |
| env: | |
| SNAP_STORE_CREDENTIALS_B64: ${{ secrets.SNAP_STORE_CREDENTIALS }} | |
| run: | | |
| set +x # Prevent command echoing | |
| if [ -z "$SNAP_STORE_CREDENTIALS_B64" ]; then | |
| echo "SNAP_STORE_CREDENTIALS not set, skipping registration" | |
| exit 0 | |
| fi | |
| # Decode credentials to temp file | |
| CREDS_FILE=$(mktemp) | |
| echo "$SNAP_STORE_CREDENTIALS_B64" | base64 -d > "$CREDS_FILE" 2>/dev/null | |
| # Unset env var and use file for login | |
| unset SNAPCRAFT_STORE_CREDENTIALS | |
| snapcraft login --with "$CREDS_FILE" || echo "Already logged in" | |
| snapcraft register shelldock || echo "Snap name already registered or registration failed" | |
| shred -u "$CREDS_FILE" 2>/dev/null || rm -f "$CREDS_FILE" | |
| continue-on-error: true | |
| - name: Publish to Snap Store | |
| env: | |
| SNAP_STORE_CREDENTIALS_B64: ${{ secrets.SNAP_STORE_CREDENTIALS }} | |
| run: | | |
| set +x # Prevent command echoing to avoid exposing credentials | |
| if [ -z "$SNAP_STORE_CREDENTIALS_B64" ]; then | |
| echo "SNAP_STORE_CREDENTIALS not set, skipping publish" | |
| exit 0 | |
| fi | |
| # Decode credentials to temp file | |
| CREDS_FILE=$(mktemp) | |
| echo "$SNAP_STORE_CREDENTIALS_B64" | base64 -d > "$CREDS_FILE" 2>/dev/null | |
| # Unset env var before login (required by snapcraft) | |
| unset SNAPCRAFT_STORE_CREDENTIALS | |
| # Login using credentials file | |
| snapcraft login --with "$CREDS_FILE" || echo "Login failed or already logged in" | |
| SNAP_FILE=$(ls *.snap | head -1) | |
| if [ -n "$SNAP_FILE" ]; then | |
| snapcraft upload "$SNAP_FILE" --release stable || echo "Publish failed (may already be published)" | |
| else | |
| echo "No snap file found to upload" | |
| fi | |
| # Securely remove temp file | |
| shred -u "$CREDS_FILE" 2>/dev/null || rm -f "$CREDS_FILE" | |
| continue-on-error: true | |
| - name: Upload snap | |
| 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/ | |
| cat > flatpak/com.github.opsguild.shelldock.yml << 'EOF' | |
| app-id: com.github.opsguild.shelldock | |
| runtime: org.freedesktop.Platform | |
| runtime-version: '22.08' | |
| sdk: org.freedesktop.Sdk | |
| command: shelldock | |
| finish-args: | |
| - --share=network | |
| - --filesystem=home | |
| modules: | |
| - name: shelldock | |
| buildsystem: simple | |
| build-commands: | |
| - install -Dm755 shelldock-linux-amd64 /app/bin/shelldock | |
| sources: | |
| - type: file | |
| path: shelldock-linux-amd64 | |
| EOF | |
| - 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: Commit and push formula to main repo | |
| run: | | |
| git config user.name "github-actions[bot]" | |
| git config user.email "github-actions[bot]@users.noreply.github.com" | |
| git add Formula/shelldock.rb | |
| git diff --staged --quiet || git commit -m "Update Homebrew formula to ${{ github.ref_name }}" | |
| git push origin HEAD:main || git push origin HEAD:master | |
| - name: Push to Homebrew tap (if repository exists) | |
| env: | |
| HOMEBREW_TAP_TOKEN: ${{ secrets.HOMEBREW_TAP_TOKEN }} | |
| run: | | |
| 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 | |
| # Clone tap repository | |
| git clone https://${HOMEBREW_TAP_TOKEN}@github.com/OpsGuild/homebrew-tap.git homebrew-tap || { | |
| echo "Homebrew tap repository not found. Create it at https://github.com/OpsGuild/homebrew-tap" | |
| exit 0 | |
| } | |
| cd homebrew-tap | |
| # 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 | |
| git diff --staged --quiet || git commit -m "Update shelldock to ${{ github.ref_name }}" | |
| git push origin main || git push origin master | |
| 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 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: | | |
| cat > packaging/arch/PKGBUILD << EOF | |
| # Maintainer: OpsGuild | |
| pkgname=shelldock | |
| pkgver=${{ steps.version.outputs.version }} | |
| pkgrel=1 | |
| pkgdesc="A fast, cross-platform shell command repository manager" | |
| arch=('x86_64' 'aarch64') | |
| url="https://github.com/OpsGuild/ShellDock" | |
| license=('MIT') | |
| source_x86_64=("https://github.com/OpsGuild/ShellDock/releases/download/${{ github.ref_name }}/shelldock-linux-amd64") | |
| source_aarch64=("https://github.com/OpsGuild/ShellDock/releases/download/${{ github.ref_name }}/shelldock-linux-arm64") | |
| sha256sums_x86_64=('${{ steps.sha256.outputs.sha256 }}') | |
| sha256sums_aarch64=('SKIP') | |
| package() { | |
| install -Dm755 "\${srcdir}/shelldock-linux-\${CARCH}" "\${pkgdir}/usr/bin/shelldock" | |
| install -Dm644 "\${srcdir}/README.md" "\${pkgdir}/usr/share/doc/shelldock/README.md" | |
| install -d "\${pkgdir}/usr/share/shelldock/repository" | |
| cp "\${srcdir}/repository"/*.yaml "\${pkgdir}/usr/share/shelldock/repository/" 2>/dev/null || true | |
| } | |
| EOF | |
| - name: Generate .SRCINFO | |
| run: | | |
| cd packaging/arch | |
| # Use Docker with Arch Linux to generate .SRCINFO properly | |
| docker run --rm -v "$(pwd):/pkg" -w /pkg archlinux/base:latest bash -c " | |
| pacman -Sy --noconfirm base-devel | |
| makepkg --printsrcinfo > .SRCINFO | |
| " || { | |
| # Fallback: Generate .SRCINFO manually if Docker fails | |
| source PKGBUILD | |
| { | |
| echo "pkgbase = $pkgname" | |
| echo " pkgdesc = $pkgdesc" | |
| echo " pkgver = $pkgver" | |
| echo " pkgrel = $pkgrel" | |
| echo " url = $url" | |
| echo " arch = ${arch[@]}" | |
| echo " license = ${license[@]}" | |
| for i in "${!source_x86_64[@]}"; do | |
| echo " source_x86_64 = ${source_x86_64[$i]}" | |
| done | |
| for i in "${!source_aarch64[@]}"; do | |
| echo " source_aarch64 = ${source_aarch64[$i]}" | |
| done | |
| for i in "${!sha256sums_x86_64[@]}"; do | |
| echo " sha256sums_x86_64 = ${sha256sums_x86_64[$i]}" | |
| done | |
| for i in "${!sha256sums_aarch64[@]}"; do | |
| echo " sha256sums_aarch64 = ${sha256sums_aarch64[$i]}" | |
| done | |
| } > .SRCINFO | |
| } | |
| - name: Publish to AUR | |
| env: | |
| AUR_SSH_KEY: ${{ secrets.AUR_SSH_KEY }} | |
| run: | | |
| if [ -z "$AUR_SSH_KEY" ]; then | |
| echo "AUR_SSH_KEY not set, skipping AUR publish" | |
| exit 0 | |
| fi | |
| # Setup SSH | |
| mkdir -p ~/.ssh | |
| echo "$AUR_SSH_KEY" > ~/.ssh/aur_key | |
| chmod 600 ~/.ssh/aur_key | |
| ssh-keyscan aur.archlinux.org >> ~/.ssh/known_hosts | |
| # Configure git | |
| git config --global user.name "github-actions[bot]" | |
| git config --global user.email "github-actions[bot]@users.noreply.github.com" | |
| # Clone AUR repository | |
| GIT_SSH_COMMAND="ssh -i ~/.ssh/aur_key" git clone ssh://aur@aur.archlinux.org/shelldock.git aur-repo || echo "AUR repo may not exist yet" | |
| if [ -d aur-repo ]; then | |
| cd aur-repo | |
| # Copy PKGBUILD and .SRCINFO | |
| cp ../packaging/arch/PKGBUILD . | |
| cp ../packaging/arch/.SRCINFO . | |
| # Commit and push | |
| git add PKGBUILD .SRCINFO | |
| git diff --staged --quiet || (git commit -m "Update to ${{ github.ref_name }}" && GIT_SSH_COMMAND="ssh -i ~/.ssh/aur_key" git push origin master) | |
| fi | |
| 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 }} |