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
102 changes: 102 additions & 0 deletions .github/workflows/huggingfaceTests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
name: HuggingFace Tests
on:
workflow_dispatch:
push:
branches:
- "master"
# Triggers the workflow on PRs to master branch only.
pull_request_target:
types: [labeled]
branches:
- "master"

# Ensures that only the latest commit is running for each PR at a time.
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.sha }}-${{ github.ref }}
cancel-in-progress: true
permissions:
id-token: write
contents: read
jobs:
HuggingFace-Tests:
name: HuggingFace tests (${{ matrix.os.name }})
if: github.event_name == 'workflow_dispatch' || github.event_name == 'push' || contains(github.event.pull_request.labels.*.name, 'safe to test')
strategy:
fail-fast: false
matrix:
os:
- name: ubuntu
version: 24.04
- name: windows
version: 2022
- name: macos
version: 14
runs-on: ${{ matrix.os.name }}-${{ matrix.os.version }}
steps:
- name: Skip macOS - JGC-413
if: matrix.os.name == 'macos'
run: |
echo "::warning::JGC-413 - Skip until artifactory bootstrap in osx is fixed"
exit 0

- name: Checkout code
if: matrix.os.name != 'macos'
uses: actions/checkout@v5
with:
ref: ${{ github.event.pull_request.head.sha || github.ref }}

- name: Setup FastCI
if: matrix.os.name != 'macos'
uses: jfrog-fastci/fastci@v0
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
fastci_otel_token: ${{ secrets.FASTCI_TOKEN }}

- name: Setup Go with cache
if: matrix.os.name != 'macos'
uses: jfrog/.github/actions/install-go-with-cache@main

- name: Setup Python
if: matrix.os.name != 'macos'
uses: actions/setup-python@v5
with:
python-version: '3.11'

- name: Install HuggingFace CLI
if: matrix.os.name != 'macos'
run: |
pip install huggingface_hub
shell: bash

- name: Debug macOS Environment and Set Timeout
if: runner.os == 'macOS'
run: |
echo "=== macOS Debug Information ==="
echo "Architecture: $(uname -m)"
echo "macOS Version: $(sw_vers -productVersion)"
echo "macOS Build: $(sw_vers -buildVersion)"
echo "Available memory: $(system_profiler SPHardwareDataType | grep Memory || echo 'Memory info not available')"
echo "Available disk space: $(df -h)"
echo "Go version: $(go version)"
echo "Setting RT_CONNECTION_TIMEOUT_SECONDS to 2400 for macOS"
echo "RT_CONNECTION_TIMEOUT_SECONDS=2400" >> $GITHUB_ENV

- name: Install local Artifactory
if: matrix.os.name != 'macos'
uses: jfrog/.github/actions/install-local-artifactory@main
with:
RTLIC: ${{ secrets.RTLIC }}
RT_CONNECTION_TIMEOUT_SECONDS: ${{ env.RT_CONNECTION_TIMEOUT_SECONDS || '1200' }}

- name: Get ID Token and Exchange Token
if: matrix.os.name != 'macos'
shell: bash
run: |
ID_TOKEN=$(curl -sLS -H "User-Agent: actions/oidc-client" -H "Authorization: Bearer $ACTIONS_ID_TOKEN_REQUEST_TOKEN" \
"${ACTIONS_ID_TOKEN_REQUEST_URL}&audience=jfrog-github" | jq .value | tr -d '"')
echo "JFROG_CLI_OIDC_EXCHANGE_TOKEN_ID=${ID_TOKEN}" >> $GITHUB_ENV

- name: Run HuggingFace tests
if: matrix.os.name != 'macos'
run: go test -v github.com/jfrog/jfrog-cli --timeout 0 --test.huggingface --jfrog.url=http://127.0.0.1:8082 --jfrog.adminToken=${{ env.JFROG_TESTS_LOCAL_ACCESS_TOKEN }}

87 changes: 87 additions & 0 deletions buildtools/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import (
"github.com/jfrog/jfrog-cli-artifactory/artifactory/commands/golang"
"github.com/jfrog/jfrog-cli-artifactory/artifactory/commands/gradle"
helmcmd "github.com/jfrog/jfrog-cli-artifactory/artifactory/commands/helm"
huggingfaceCommands "github.com/jfrog/jfrog-cli-artifactory/artifactory/commands/huggingface"
"github.com/jfrog/jfrog-cli-artifactory/artifactory/commands/mvn"
"github.com/jfrog/jfrog-cli-artifactory/artifactory/commands/npm"
containerutils "github.com/jfrog/jfrog-cli-artifactory/artifactory/commands/ocicontainer"
Expand Down Expand Up @@ -58,6 +59,7 @@ import (
"github.com/jfrog/jfrog-cli/docs/buildtools/gopublish"
gradledoc "github.com/jfrog/jfrog-cli/docs/buildtools/gradle"
"github.com/jfrog/jfrog-cli/docs/buildtools/gradleconfig"
"github.com/jfrog/jfrog-cli/docs/buildtools/huggingface"
mvndoc "github.com/jfrog/jfrog-cli/docs/buildtools/mvn"
"github.com/jfrog/jfrog-cli/docs/buildtools/mvnconfig"
"github.com/jfrog/jfrog-cli/docs/buildtools/npmcommand"
Expand Down Expand Up @@ -475,6 +477,17 @@ func GetCommands() []cli.Command {
Category: buildToolsCategory,
Action: twineCmd,
},
{
Name: "hugging-face",
Aliases: []string{"hf"},
Flags: cliutils.GetCommandFlags(cliutils.HuggingFace),
HelpName: corecommon.CreateUsage("hugging-face", huggingface.GetDescription(), huggingface.Usage),
Description: huggingface.GetDescription(),
UsageText: huggingface.GetArguments(),
Hidden: true,
Action: huggingFaceCmd,
Category: buildToolsCategory,
},
})
return decorateWithFlagCapture(cmds)
}
Expand Down Expand Up @@ -1115,6 +1128,80 @@ func loginCmd(c *cli.Context) error {
return nil
}

func huggingFaceCmd(c *cli.Context) error {
if show, err := cliutils.ShowCmdHelpIfNeeded(c, c.Args()); show || err != nil {
return err
}
if c.NArg() < 1 {
return cliutils.WrongNumberOfArgumentsHandler(c)
}
args := cliutils.ExtractCommand(c)
cmdName, hfArgs := getCommandName(args)
switch cmdName {
case "u", "upload":
return huggingFaceUploadCmd(c, hfArgs)
case "d", "download":
return huggingFaceDownloadCmd(c, hfArgs)
default:
return errorutils.CheckErrorf("unknown HuggingFace command: '%s'. Valid commands are: upload (u), download (d)", cmdName)
}
}

func huggingFaceUploadCmd(c *cli.Context, hfArgs []string) error {
// Upload requires folderPath and repoID
if len(hfArgs) < 2 {
return cliutils.PrintHelpAndReturnError("Folder path and repository ID are required.", c)
}
folderPath := hfArgs[0]
if folderPath == "" {
return cliutils.PrintHelpAndReturnError("Folder path cannot be empty.", c)
}
repoID := hfArgs[1]
if repoID == "" {
return cliutils.PrintHelpAndReturnError("Repository ID cannot be empty.", c)
}
revision := ""
if c.String("revision") != "" {
revision = c.String("revision")
}
repoType := c.String("repo-type")
if repoType == "" {
repoType = "model"
}
huggingFaceUploadCmd := huggingfaceCommands.NewHuggingFaceUpload().SetFolderPath(folderPath).SetRepoId(repoID).SetRepoType(repoType).SetRevision(revision)
return commands.Exec(huggingFaceUploadCmd)
}

func huggingFaceDownloadCmd(c *cli.Context, hfArgs []string) error {
// Download requires repoID
if len(hfArgs) < 1 {
return cliutils.PrintHelpAndReturnError("Model/Dataset name is required.", c)
}
const eTagTimeout = 86400
repoID := hfArgs[0]
if repoID == "" {
return cliutils.PrintHelpAndReturnError("Model/Dataset name cannot be empty.", c)
}
revision := ""
if c.String("revision") != "" {
revision = c.String("revision")
}
etagTimeout := eTagTimeout
if c.String("etag-timeout") != "" {
var err error
etagTimeout, err = strconv.Atoi(c.String("etag-timeout"))
if err != nil {
return errorutils.CheckErrorf("invalid etag-timeout value: %s", c.String("etag-timeout"))
}
}
repoType := c.String("repo-type")
if repoType == "" {
repoType = "model"
}
Comment on lines +1197 to +1200
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Default values for repoType are set in the command functions, but the flag definition says "[Default: model]" which suggests it should be set at the flag level.
Default should be in the flag definition, not in the command logic. This is inconsistent with how other flags work.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NA.

huggingFaceDownloadCmd := huggingfaceCommands.NewHuggingFaceDownload().SetRepoId(repoID).SetRepoType(repoType).SetRevision(revision).SetEtagTimeout(etagTimeout)
return commands.Exec(huggingFaceDownloadCmd)
}

func dockerScanCmd(c *cli.Context, imageTag string) error {
if show, err := cliutils.ShowGenericCmdHelpIfNeeded(c, c.Args(), securityCLI.DockerScanCmdHiddenName); show || err != nil {
return err
Expand Down
32 changes: 32 additions & 0 deletions docs/buildtools/huggingface/help.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package huggingface

var Usage = []string{"hf d <model-name>",
"hf u <folder-path> <repo-id>"}

func GetDescription() string {
return `Download or upload models/datasets from/to HuggingFace Hub.`
}

func GetArguments() string {
return ` d <model-name>
Download a model/dataset from HuggingFace Hub.
model-name
The HuggingFace model repository ID (e.g., 'bert-base-uncased' or 'username/model-name').

u <folder-path> <repo-id>
Upload a model or dataset folder to HuggingFace Hub.
folder-path
Path to the folder to upload.
repo-id
The HuggingFace repository ID (e.g., 'username/model-name' or 'username/dataset-name').

Command options:
--revision
[Optional] The revision (commit hash, branch name, or tag) to download/upload. Defaults to main branch if not specified.

--repo-type
[Optional] The repository type. Can be 'model' or 'dataset'. Default: 'model'.

--etag-timeout
[Optional] [Download only] Timeout in seconds for ETag validation. Default: 86400 seconds (24 hours).`
}
4 changes: 3 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ require (
github.com/jfrog/build-info-go v1.13.1-0.20260130140656-2d0d5593fccf
github.com/jfrog/gofrog v1.7.6
github.com/jfrog/jfrog-cli-application v1.0.2-0.20260202095705-3657239cde88
github.com/jfrog/jfrog-cli-artifactory v0.8.1-0.20260206111107-7a05b41040dd
github.com/jfrog/jfrog-cli-artifactory v0.8.1-0.20260210060138-bbe22834d469
github.com/jfrog/jfrog-cli-core/v2 v2.60.1-0.20260202114755-cec5fd702f50
github.com/jfrog/jfrog-cli-evidence v0.8.3-0.20260202100913-d9ee9476845a
github.com/jfrog/jfrog-cli-platform-services v1.10.1-0.20251205121610-171eb9b0000e
Expand Down Expand Up @@ -257,6 +257,8 @@ replace github.com/gfleury/go-bitbucket-v1 => github.com/gfleury/go-bitbucket-v1

replace github.com/ktrysmt/go-bitbucket => github.com/ktrysmt/go-bitbucket v0.9.80

//replace github.com/jfrog/jfrog-cli-artifactory => github.com/naveenku-jfrog/jfrog-cli-artifactory v0.0.0-20260205125834-9726cdd29d6d

//replace github.com/jfrog/jfrog-cli-core/v2 => ../jfrog-cli-core

// replace github.com/jfrog/jfrog-cli-artifactory => github.com/fluxxBot/jfrog-cli-artifactory v0.0.0-20260130044429-464a5025d08a
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -419,8 +419,8 @@ github.com/jfrog/jfrog-apps-config v1.0.1 h1:mtv6k7g8A8BVhlHGlSveapqf4mJfonwvXYL
github.com/jfrog/jfrog-apps-config v1.0.1/go.mod h1:8AIIr1oY9JuH5dylz2S6f8Ym2MaadPLR6noCBO4C22w=
github.com/jfrog/jfrog-cli-application v1.0.2-0.20260202095705-3657239cde88 h1:KP6SmrduuGMMsMfHhBATv5I3W1iTtjBojtqEkPHBWk4=
github.com/jfrog/jfrog-cli-application v1.0.2-0.20260202095705-3657239cde88/go.mod h1:xum2HquWO5uExa/A7MQs3TgJJVEeoqTR+6Z4mfBr1Xw=
github.com/jfrog/jfrog-cli-artifactory v0.8.1-0.20260206111107-7a05b41040dd h1:ZKRwlPEkSmul7fBKat7qBVU3tSxd6xg2GbxQixK4COg=
github.com/jfrog/jfrog-cli-artifactory v0.8.1-0.20260206111107-7a05b41040dd/go.mod h1:1GKFtQs/5/3HRp1JyMHMQVT9lxzXR+YecO6dWdRThNs=
github.com/jfrog/jfrog-cli-artifactory v0.8.1-0.20260210060138-bbe22834d469 h1:uCC03HffS+KPfd9rmW61jAmv7fi8FyJwUrw4FfioIYM=
github.com/jfrog/jfrog-cli-artifactory v0.8.1-0.20260210060138-bbe22834d469/go.mod h1:1GKFtQs/5/3HRp1JyMHMQVT9lxzXR+YecO6dWdRThNs=
github.com/jfrog/jfrog-cli-core/v2 v2.60.1-0.20260202114755-cec5fd702f50 h1:u3NvevCPC5ygeOMv39XSlpIOCt7PhLtFWR2XZ0QkKaU=
github.com/jfrog/jfrog-cli-core/v2 v2.60.1-0.20260202114755-cec5fd702f50/go.mod h1:+Hnaikp/xCSPD/q7txxRy4Zc0wzjW/usrCSf+6uONSQ=
github.com/jfrog/jfrog-cli-evidence v0.8.3-0.20260202100913-d9ee9476845a h1:lTOAhUjKcOmM/0Kbj4V+I/VHPlW7YNAhIEVpGnCM5mI=
Expand Down
Loading
Loading