diff --git a/.devcontainer/devcontainer-with-cvmfs.json b/.devcontainer/devcontainer-with-cvmfs.json new file mode 100644 index 0000000..5f61422 --- /dev/null +++ b/.devcontainer/devcontainer-with-cvmfs.json @@ -0,0 +1,8 @@ +{ + "name": "CVMFS Action Test", + "image": "mcr.microsoft.com/devcontainers/base:ubuntu", + "features": { + "ghcr.io/cvmfs-contrib/github-action-cvmfs/cvmfs": {} + }, + "forwardPorts": [] +} \ No newline at end of file diff --git a/COPILOT_INSTRUCTION.md b/.github/COPILOT_INSTRUCTION.md similarity index 100% rename from COPILOT_INSTRUCTION.md rename to .github/COPILOT_INSTRUCTION.md diff --git a/.github/workflows/package-feature.yml b/.github/workflows/package-feature.yml new file mode 100644 index 0000000..921891e --- /dev/null +++ b/.github/workflows/package-feature.yml @@ -0,0 +1,14 @@ +name: Package Devcontainer Feature +on: [push, pull_request] +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + - name: Install Devcontainer CLI + run: npm install -g @devcontainers/cli + - name: Package CVMFS Feature + run: devcontainer features package src/cvmfs/ + - name: Generate CVMFS Feature Docs + run: devcontainer features generate-docs -p src/ -n cvmfs-contrib/cvmfs \ No newline at end of file diff --git a/.github/workflows/publish-feature.yml b/.github/workflows/publish-feature.yml new file mode 100644 index 0000000..fa9995a --- /dev/null +++ b/.github/workflows/publish-feature.yml @@ -0,0 +1,40 @@ +name: Publish Devcontainer Feature + +on: + workflow_call: + inputs: + version: + description: 'The version to publish' + required: true + type: string + release: + types: [created] + +jobs: + publish: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Install Devcontainer CLI + run: npm install -g @devcontainers/cli + + - name: Login to GitHub Container Registry + run: echo "${{ secrets.WRITE_PACKAGES_TOKEN }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin + + - name: Add version to environment + run: | + VERSION="${{ inputs.version || github.ref_name }}" + echo "VERSION=$VERSION" >> $GITHUB_ENV + + - name: Add version to devcontainer-feature.json + run: | + jq --arg VERSION "${{ env.VERSION }}" '.version = $VERSION' src/cvmfs/devcontainer-feature.json > tmp.json + mv tmp.json src/cvmfs/devcontainer-feature.json + + - name: Publish Devcontainer Feature + run: devcontainer features publish --namespace ${{ github.actor }}/${{ github.repository }} . + + - name: Build Devcontainer Feature + run: devcontainer build --workspace-folder . --image-name test-cvmfs-feature --additional-features ghcr.io/${{ github.actor }}/${{ github.repository }}/cvmfs:${{ env.VERSION }} diff --git a/README.devcontainer.md b/README.devcontainer.md new file mode 100644 index 0000000..a71bb70 --- /dev/null +++ b/README.devcontainer.md @@ -0,0 +1,39 @@ +# CVMFS Devcontainer Feature + +This directory contains a devcontainer feature for installing and configuring the CVMFS client. + +## Publishing to GHCR + +This feature is intended to be published to the GitHub Container Registry (GHCR) to be easily reusable by other projects. + +### Manual Publishing + +1. **Install the Devcontainer CLI:** + ```bash + npm install -g @devcontainers/cli + ``` + +2. **Create a Personal Access Token (PAT):** + * Go to GitHub **Settings** > **Developer settings** > **Personal access tokens** > **Tokens (classic)**. + * Generate a new token with the `write:packages` scope. + * Export the token as an environment variable: + ```bash + export WRITE_PACKAGES_TOKEN=your-personal-access-token + ``` + +3. **Log in to GHCR:** + ```bash + echo $WRITE_PACKAGES_TOKEN | docker login ghcr.io -u your-github-username --password-stdin + ``` + +4. **Publish the Feature:** + Run the following command from the root of this repository. The namespace should match the GitHub organization (`cvmfs-contrib`). + ```bash + devcontainer features publish src/cvmfs --namespace cvmfs-contrib/ + ``` + +### Automated Publishing + +This repository is configured with a GitHub Actions workflow to automate this process. When a new release is published on GitHub, the workflow will automatically build and publish the feature to GHCR. + +For this to work, the `WRITE_PACKAGES_TOKEN` secret (a Personal Access Token with `write:packages` scope) must be configured in the repository's **Settings > Secrets and variables > Actions**. diff --git a/README.md b/README.md index c2c5fad..9befb03 100644 --- a/README.md +++ b/README.md @@ -133,6 +133,27 @@ jobs: This GitHub Action installs the [CernVM-FS package](https://cernvm.cern.ch/fs/#download), and configures it with the `CVMFS_CLIENT_PROFILE='single'` and `CVMFS_USE_CDN='yes'` to avoid any overhead on the CernVM-FS stratum 1 servers that you are accessing, this GitHub Action uses the [OpenHTC](https://openhtc.io) caching edge servers. +## Devcontainer Usage + +This repository includes a pre-configured devcontainer for a consistent development environment. It uses the CVMFS devcontainer feature located in the `src/cvmfs` directory. + +To use it, open this repository in a devcontainer-compatible editor like VS Code with the Dev Containers extension. + +You can add other functionalities to your devcontainer by including more features. A comprehensive list of contributed features can be found in the [devcontainer feature index](https://containers.dev/features). + +For example, you can add the following to your `devcontainer.json` to use this feature: +```json +{ + "name": "CVMFS Action Dev", + "image": "mcr.microsoft.com/devcontainers/base:ubuntu", + "features": { + "ghcr.io/cvmfs-contrib/features/cvmfs:1": { + "CVMFS_REPOSITORIES": "alice.cern.ch,atlas.cern.ch" + } + } +} +``` + ## Limitations This GitHub Action is only expected to work in workflows that [run on](https://docs.github.com/en/free-pro-team@latest/actions/reference/workflow-syntax-for-github-actions#jobsjob_idruns-on) ubuntu. There is experimental support for `macOS` (11+). diff --git a/action.yml b/action.yml index 1e3e99f..7587b01 100644 --- a/action.yml +++ b/action.yml @@ -357,7 +357,14 @@ runs: path: | ${{ inputs.apt_cache }} - run: | - ${{ github.action_path }}/setup-cvmfs.sh + if [ "$RUNNER_OS" == "Linux" ]; then + bash ${{ github.action_path }}/setup-cvmfs-linux.sh + elif [ "$RUNNER_OS" == "macOS" ]; then + bash ${{ github.action_path }}/setup-cvmfs-macos.sh + else + echo "Unsupported platform: $RUNNER_OS" + exit 1 + fi shell: bash env: ACTION_PATH: ${{ github.action_path }} diff --git a/createConfig.sh b/create-config.sh similarity index 94% rename from createConfig.sh rename to create-config.sh index 8857c8f..d668e3d 100755 --- a/createConfig.sh +++ b/create-config.sh @@ -79,11 +79,11 @@ declare -a vars=(CVMFS_ALIEN_CACHE CVMFS_WORKSPACE ) -sudo mkdir -p /etc/cvmfs -echo "# cvmfs default.local file installed with cvmfs-contrib/github-action-cvmfs" | sudo tee /etc/cvmfs/default.local +mkdir -p /etc/cvmfs +echo "# cvmfs default.local file installed with cvmfs-contrib/github-action-cvmfs" | tee /etc/cvmfs/default.local for var_name in "${vars[@]}" do if [ ! -z "$(eval "echo \$$var_name")" ]; then - echo "$var_name='$(eval "echo \$$var_name")'" | sudo tee -a /etc/cvmfs/default.local + echo "$var_name='$(eval "echo \$$var_name")'" | tee -a /etc/cvmfs/default.local fi done diff --git a/devcontainer-feature.json b/devcontainer-feature.json new file mode 100644 index 0000000..0cc1901 --- /dev/null +++ b/devcontainer-feature.json @@ -0,0 +1,24 @@ +{ + "name": "CVMFS", + "id": "cvmfs", + "version": "1.4.3", + "description": "Installs CVMFS client", + "options": { + "CVMFS_REPOSITORIES": { + "type": "string", + "default": "sft.cern.ch", + "description": "Comma-separated list of fully qualified repository names that shall be mountable under /cvmfs" + }, + "CVMFS_CONFIG_PACKAGE": { + "type": "string", + "default": "cvmfs-config-default", + "description": "URL to the cvmfs config package to install" + }, + "CVMFS_HTTP_PROXY": { + "type": "string", + "default": "DIRECT", + "description": "Chain of HTTP proxy groups used by CernVM-FS. Defaults to DIRECT)" + } + }, + "entrypoint": "install-cvmfs-linux.sh" +} diff --git a/install-cvmfs-linux.sh b/install-cvmfs-linux.sh new file mode 100644 index 0000000..05484cd --- /dev/null +++ b/install-cvmfs-linux.sh @@ -0,0 +1,42 @@ +#!/usr/bin/env bash +# Shared CVMFS install and config logic for Linux +set -e + +# Install CVMFS release package +CVMFS_UBUNTU_DEB_LOCATION=${CVMFS_UBUNTU_DEB_LOCATION:-https://ecsft.cern.ch/dist/cvmfs/cvmfs-release/cvmfs-release-latest_all.deb} +APT_ARCHIVES=/var/cache/apt/archives/ +mkdir -p ${APT_ARCHIVES} +if [ ! -f ${APT_ARCHIVES}/cvmfs-release-latest_all.deb ] ; then + curl -L -o ${APT_ARCHIVES}/cvmfs-release-latest_all.deb ${CVMFS_UBUNTU_DEB_LOCATION} +fi +dpkg -i ${APT_ARCHIVES}/cvmfs-release-latest_all.deb + +# Install CVMFS and config package +apt-get -q update +if [ "${CVMFS_CONFIG_PACKAGE}" == "cvmfs-config-default" ]; then + apt-get -q -y install cvmfs cvmfs-config-default +else + curl -L -o ${APT_ARCHIVES}/cvmfs-config.deb ${CVMFS_CONFIG_PACKAGE} + dpkg -i ${APT_ARCHIVES}/cvmfs-config.deb +fi + +# Write config from environment variables +mkdir -p /etc/cvmfs + +echo "# cvmfs default.local file installed by cvmfs-install-core.sh" > /etc/cvmfs/default.local +for var_name in $(compgen -A variable | grep ^CVMFS_) +do + if [ -n "$(eval echo \$$var_name)" ]; then + echo "$var_name='$(eval echo \$$var_name)'" >> /etc/cvmfs/default.local + fi +done + +# Configure CVMFS +bash create-config.sh +bash setup-cvmfs-config.sh + +# Mount repositories +for repo in $(echo ${CVMFS_REPOSITORIES} | sed "s/,/ /g") +do + sudo mount -t cvmfs ${repo} /cvmfs/${repo} +done diff --git a/setup-cvmfs-config.sh b/setup-cvmfs-config.sh new file mode 100755 index 0000000..33adae7 --- /dev/null +++ b/setup-cvmfs-config.sh @@ -0,0 +1,13 @@ +#!/usr/bin/env bash +# CVMFS configuration logic +set -e + +echo "::group::Running cvmfs_config setup" +sudo cvmfs_config setup +retConfig=$? +if [ $retConfig -ne 0 ]; then + echo "!!! github-action-cvmfs FAILED !!!" + echo "cvmfs_config setup exited with ${retConfig}" + exit $retConfig +fi +echo "::endgroup::" diff --git a/setup-cvmfs-linux.sh b/setup-cvmfs-linux.sh new file mode 100644 index 0000000..29e8b93 --- /dev/null +++ b/setup-cvmfs-linux.sh @@ -0,0 +1,22 @@ +#!/usr/bin/env bash +# Linux-specific wrapper for CI/CD or local install +set -e + +# Optional: handle APT cache for CI/CD +if [ -n "${APT_CACHE}" ]; then + echo "Using cache from ${APT_CACHE}" + mkdir -p ${APT_CACHE}/archives/ ${APT_CACHE}/lists/ + sudo cp -r ${APT_CACHE}/archives /var/cache/apt + sudo cp -r ${APT_CACHE}/lists /var/lib/apt +fi + +# Call shared install logic (preserve environment with configuration) +sudo -E bash "$(dirname "$0")/install-cvmfs-linux.sh" + +# Optional: update cache after install +if [ -n "${APT_CACHE}" ]; then + echo "Updating cache to ${APT_CACHE}" + mkdir -p ${APT_CACHE}/archives/ ${APT_CACHE}/lists/ + cp /var/cache/apt/archives/*.deb ${APT_CACHE}/archives/ + cp /var/lib/apt/lists/*_dists_* ${APT_CACHE}/lists/ +fi diff --git a/setup-cvmfs-macos.sh b/setup-cvmfs-macos.sh new file mode 100644 index 0000000..23b5e10 --- /dev/null +++ b/setup-cvmfs-macos.sh @@ -0,0 +1,29 @@ +#!/usr/bin/env bash +# macOS-specific CVMFS install logic +set -e + +echo "warning: MacOS support is experimental." + +brew tap macos-fuse-t/cask +brew tap cvmfs/homebrew-cvmfs +brew install cvmfs + +# Create /cvmfs mountpoint using synthetic firmlink +sudo zsh -c 'echo -e "cvmfs\tUsers/Shared/cvmfs\n#comment\n" > /etc/synthetic.conf' +sudo chown root:wheel /etc/synthetic.conf +sudo chmod a+r /etc/synthetic.conf +sudo /System/Library/Filesystems/apfs.fs/Contents/Resources/apfs.util -t || true + +# Configure CVMFS +bash create-config.sh +bash setup-cvmfs-config.sh + +# Mount repositories +for repo in $(echo ${CVMFS_REPOSITORIES} | sed "s/,/ /g") +do + mkdir -p /Users/Shared/cvmfs/${repo} + sudo mount -t cvmfs ${repo} /Users/Shared/cvmfs/${repo} +done + +# Fuse-t can have a brief lag after mounting before the mountpoint responds +sleep 3 \ No newline at end of file diff --git a/setup-cvmfs.sh b/setup-cvmfs.sh deleted file mode 100755 index 8389158..0000000 --- a/setup-cvmfs.sh +++ /dev/null @@ -1,88 +0,0 @@ -#!/usr/bin/env bash - -# Platform specific install -if [ "$(uname)" == "Linux" ]; then - # download from cache - if [ -n "${APT_CACHE}" ]; then - echo "::group::Using cache" - echo "Copying cache from ${APT_CACHE} to system locations..." - mkdir -p ${APT_CACHE}/archives/ ${APT_CACHE}/lists/ - sudo cp -r ${APT_CACHE}/archives /var/cache/apt - sudo cp -r ${APT_CACHE}/lists /var/lib/apt - echo "::endgroup::" - fi - # install cvmfs release package - echo "::group::Installing cvmfs-release" - APT_ARCHIVES=/var/cache/apt/archives/ - if [ ! -f ${APT_ARCHIVES}/cvmfs-release-latest_all.deb ] ; then - sudo curl -L -o ${APT_ARCHIVES}/cvmfs-release-latest_all.deb ${CVMFS_UBUNTU_DEB_LOCATION} - fi - sudo dpkg -i ${APT_ARCHIVES}/cvmfs-release-latest_all.deb - echo "::endgroup::" - # install cvmfs package - echo "::group::Installing cvmfs" - sudo rm -f /var/lib/man-db/auto-update - sudo apt-get -q update - sudo apt-get -q -y install cvmfs - # install cvmfs config package - if [ "${CVMFS_CONFIG_PACKAGE}" == "cvmfs-config-default" ]; then - sudo apt-get -q -y install cvmfs-config-default - else - sudo curl -L -o ${APT_ARCHIVES}/cvmfs-config.deb ${CVMFS_CONFIG_PACKAGE} - sudo dpkg -i ${APT_ARCHIVES}/cvmfs-config.deb - fi - sudo touch /var/lib/man-db/auto-update - echo "::endgroup::" - # update cache (avoid restricted partial directories) - if [ -n "${APT_CACHE}" ]; then - echo "::group::Updating cache" - echo "Copying cache from system locations to ${APT_CACHE}..." - mkdir -p ${APT_CACHE}/archives/ ${APT_CACHE}/lists/ - cp /var/cache/apt/archives/*.deb ${APT_CACHE}/archives/ - cp /var/lib/apt/lists/*_dists_* ${APT_CACHE}/lists/ - echo "::endgroup::" - fi -elif [ "$(uname)" == "Darwin" ]; then - # Warn about the phasing out of MacOS support for this action - echo "warning The CernVM-FS GitHub Action's support for MacOS \ - is still experimental." - - brew tap macos-fuse-t/cask - brew tap cvmfs/homebrew-cvmfs - brew install cvmfs - - - # / is readonly on macos 11+ - do 'synthetic firmlink' to create /cvmfs - sudo zsh -c 'echo -e "cvmfs\tUsers/Shared/cvmfs\n#comment\n" > /etc/synthetic.conf' - sudo chown root:wheel /etc/synthetic.conf - sudo chmod a+r /etc/synthetic.conf - # apfs.util seems to return non-zero error codes also on success - sudo /System/Library/Filesystems/apfs.fs/Contents/Resources/apfs.util -t || true - -else - echo "Unsupported platform" - exit 1 -fi - -${ACTION_PATH}/createConfig.sh - -echo "::group::Running cvmfs_config setup" -sudo cvmfs_config setup -retCongif=$? -if [ $retCongif -ne 0 ]; then - echo "!!! github-action-cvmfs FAILED !!!" - echo "cvmfs_config setup exited with ${retCongif}" - exit $retCongif -fi -echo "::endgroup::" - - -if [ "$(uname)" == "Darwin" ]; then - for repo in $(echo ${CVMFS_REPOSITORIES} | sed "s/,/ /g") - do - mkdir -p /Users/Shared/cvmfs/${repo} - sudo mount -t cvmfs ${repo} /Users/Shared/cvmfs/${repo} - done - # Fuse-t can have a brief lag after mounting before the mountpoint responds - sleep 3 -fi