Merge branch 'basecamp:main' into main
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: Features, Bug Reports, Questions
|
||||
url: https://github.com/basecamp/fizzy/discussions/new/choose
|
||||
about: Please use the discussions area to report issues or ask quest
|
||||
@@ -0,0 +1,8 @@
|
||||
---
|
||||
name: Pre-Discussed and Approved Topics
|
||||
about: |-
|
||||
For topics already discussed and approved in the GitHub Discussions section.
|
||||
---
|
||||
|
||||
** PLEASE START A DISCUSSION INSTEAD OF OPENING AN ISSUE **
|
||||
** For more details see CONTRIBUTING.md **
|
||||
@@ -0,0 +1,196 @@
|
||||
name: Build and publish container image to GHCR
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
tags:
|
||||
- 'v*'
|
||||
workflow_dispatch:
|
||||
|
||||
concurrency:
|
||||
group: publish-${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
id-token: write
|
||||
attestations: write
|
||||
|
||||
env:
|
||||
IMAGE_DESCRIPTION: Fizzy is Kanban as it should be. Not as it has been.
|
||||
SOURCE_URL: https://github.com/${{ github.repository }}
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Build and push image (${{ matrix.arch }})
|
||||
runs-on: ${{ matrix.runner }}
|
||||
timeout-minutes: 45
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- runner: ubuntu-latest
|
||||
platform: linux/amd64
|
||||
arch: amd64
|
||||
env:
|
||||
REGISTRY: ghcr.io
|
||||
IMAGE_NAME: ${{ github.repository }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v5.0.0
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3.11.1
|
||||
|
||||
- name: Log in to GHCR
|
||||
uses: docker/login-action@v3.5.0
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Compute canonical image name (lowercase)
|
||||
id: vars
|
||||
shell: bash
|
||||
run: |
|
||||
set -eu
|
||||
IMAGE_REF="${IMAGE_NAME:-$GITHUB_REPOSITORY}"
|
||||
CANONICAL_IMAGE="${REGISTRY}/${IMAGE_REF,,}"
|
||||
echo "canonical=${CANONICAL_IMAGE}" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Extract Docker metadata (tags, labels) with arch suffix
|
||||
id: meta
|
||||
uses: docker/metadata-action@v5.8.0
|
||||
with:
|
||||
images: ${{ steps.vars.outputs.canonical }}
|
||||
tags: |
|
||||
type=ref,event=branch
|
||||
type=ref,event=tag
|
||||
type=sha,format=short,prefix=sha-
|
||||
type=semver,pattern={{version}},enable=${{ startsWith(github.ref, 'refs/tags/v') }}
|
||||
type=semver,pattern={{major}}.{{minor}},enable=${{ startsWith(github.ref, 'refs/tags/v') }}
|
||||
type=semver,pattern={{major}},enable=${{ startsWith(github.ref, 'refs/tags/v') }}
|
||||
flavor: |
|
||||
latest=false
|
||||
suffix=-${{ matrix.arch }}
|
||||
labels: |
|
||||
org.opencontainers.image.source=${{ env.SOURCE_URL }}
|
||||
|
||||
- name: Build and push (${{ matrix.platform }})
|
||||
id: build
|
||||
uses: docker/build-push-action@v6.18.0
|
||||
with:
|
||||
context: .
|
||||
file: Dockerfile
|
||||
build-args: |
|
||||
OCI_SOURCE=${{ env.SOURCE_URL }}
|
||||
OCI_DESCRIPTION=${{ env.IMAGE_DESCRIPTION }}
|
||||
platforms: ${{ matrix.platform }}
|
||||
push: ${{ github.event_name != 'pull_request' }}
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
cache-from: type=gha,scope=${{ matrix.platform }}
|
||||
cache-to: type=gha,scope=${{ matrix.platform }},mode=max
|
||||
sbom: false
|
||||
provenance: false
|
||||
|
||||
- name: Attest image provenance (per-arch)
|
||||
if: github.event_name != 'pull_request'
|
||||
uses: actions/attest-build-provenance@v3.0.0
|
||||
with:
|
||||
subject-name: ${{ steps.vars.outputs.canonical }}
|
||||
subject-digest: ${{ steps.build.outputs.digest }}
|
||||
push-to-registry: false
|
||||
|
||||
manifest:
|
||||
name: Create multi-arch manifest and sign
|
||||
needs: build
|
||||
if: github.event_name != 'pull_request'
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 20
|
||||
env:
|
||||
REGISTRY: ghcr.io
|
||||
IMAGE_NAME: ${{ github.repository }}
|
||||
steps:
|
||||
- name: Set up Docker Buildx (for imagetools)
|
||||
uses: docker/setup-buildx-action@v3.11.1
|
||||
|
||||
- name: Log in to GHCR
|
||||
uses: docker/login-action@v3.5.0
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Compute canonical image name (lowercase)
|
||||
id: vars
|
||||
shell: bash
|
||||
run: |
|
||||
set -eu
|
||||
IMAGE_REF="${IMAGE_NAME:-$GITHUB_REPOSITORY}"
|
||||
CANONICAL_IMAGE="${REGISTRY}/${IMAGE_REF,,}"
|
||||
echo "canonical=${CANONICAL_IMAGE}" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Compute base tags (no suffix)
|
||||
id: meta
|
||||
uses: docker/metadata-action@v5.8.0
|
||||
with:
|
||||
images: ${{ steps.vars.outputs.canonical }}
|
||||
tags: |
|
||||
type=ref,event=branch
|
||||
type=ref,event=tag
|
||||
type=sha,format=short,prefix=sha-
|
||||
type=semver,pattern={{version}},enable=${{ startsWith(github.ref, 'refs/tags/v') }}
|
||||
type=semver,pattern={{major}}.{{minor}},enable=${{ startsWith(github.ref, 'refs/tags/v') }}
|
||||
type=semver,pattern={{major}},enable=${{ startsWith(github.ref, 'refs/tags/v') }}
|
||||
type=raw,value=latest,enable=${{ startsWith(github.ref, 'refs/tags/v') }}
|
||||
flavor: |
|
||||
latest=false
|
||||
labels: |
|
||||
org.opencontainers.image.source=${{ env.SOURCE_URL }}
|
||||
|
||||
- name: Create multi-arch manifests
|
||||
shell: bash
|
||||
run: |
|
||||
set -eu
|
||||
tags="${{ steps.meta.outputs.tags }}"
|
||||
echo "Creating manifests for tags:"
|
||||
printf '%s\n' "$tags"
|
||||
while IFS= read -r tag; do
|
||||
[ -z "$tag" ] && continue
|
||||
echo "Creating manifest for $tag"
|
||||
src_tag="$tag"
|
||||
if [[ "$tag" == *:latest && "${GITHUB_REF}" == refs/tags/* ]]; then
|
||||
ref="${GITHUB_REF#refs/tags/}"
|
||||
src_tag="${tag%:latest}:$ref"
|
||||
fi
|
||||
if [ -n "${IMAGE_DESCRIPTION:-}" ]; then
|
||||
docker buildx imagetools create \
|
||||
--tag "$tag" \
|
||||
--annotation "index:org.opencontainers.image.description=${IMAGE_DESCRIPTION}" \
|
||||
"${src_tag}-amd64"
|
||||
else
|
||||
docker buildx imagetools create \
|
||||
--tag "$tag" \
|
||||
"${src_tag}-amd64"
|
||||
fi
|
||||
done <<< "$tags"
|
||||
|
||||
- name: Install Cosign
|
||||
uses: sigstore/cosign-installer@v3.9.2
|
||||
|
||||
- name: Cosign sign all tags (keyless OIDC)
|
||||
shell: bash
|
||||
run: |
|
||||
set -eu
|
||||
tags="${{ steps.meta.outputs.tags }}"
|
||||
printf '%s\n' "$tags"
|
||||
while IFS= read -r tag; do
|
||||
[ -z "$tag" ] && continue
|
||||
echo "Signing $tag"
|
||||
cosign sign --yes "$tag"
|
||||
done <<< "$tags"
|
||||
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
# How to contribute to Fizzy
|
||||
|
||||
Fizzy uses GitHub
|
||||
[discussions](https://github.com/basecamp/fizzy/discussions) to track
|
||||
feature requests and questions, rather than [the issue
|
||||
tracker](https://github.com/basecamp/fizzy/issues). If you're considering
|
||||
opening an issue or pull request, please open a discussion instead.
|
||||
|
||||
Whenever a discussion leads to an actionable and well-understood task, we'll
|
||||
move it to the issue tracker where it can be worked on.
|
||||
|
||||
This is a little different than how some other projects work, but it makes it
|
||||
easier for us to triage and prioritise the work. It also means that the open
|
||||
issues all represent agreed-upon tasks that are either being worked on, or are
|
||||
ready to be worked on.
|
||||
|
||||
This should also make it easier to see what's in progress, and to find
|
||||
something to work on if you'd like to do so.
|
||||
|
||||
## What this means in practice
|
||||
|
||||
### If you'd like to contribute to the code...
|
||||
|
||||
1. If you're interested in working on one of the open issues, please do! We are
|
||||
grateful for the help!
|
||||
2. You'll want to make sure someone else isn't already working on the same
|
||||
issue. If they are, it will be tagged "in progress" and/or it should be clear
|
||||
from the comments. When in doubt, you can always comment on the issue to ask.
|
||||
3. Similarly, if you need any help or guidance on the issue, please comment on
|
||||
the issue as you go, and we'll do our best to help.
|
||||
4. When you have something ready for review or collaboration, open a PR.
|
||||
|
||||
### If you've found a bug...
|
||||
|
||||
1. If you don't have steps to reproduce the problem, or you're not certain it's a
|
||||
bug, open a discussion.
|
||||
2. If you have steps to reproduce, open an issue.
|
||||
|
||||
### If you have an idea for a feature...
|
||||
|
||||
1. Open a discussion.
|
||||
|
||||
### If you have a question, or are having trouble with configuration...
|
||||
|
||||
1. Open a discussion.
|
||||
|
||||
Hopefully this process makes it easier for everyone to be involved. Thanks for
|
||||
helping! ❤️
|
||||
|
||||
+8
-1
@@ -16,7 +16,7 @@ WORKDIR /rails
|
||||
|
||||
# Install base packages
|
||||
RUN apt-get update -qq && \
|
||||
apt-get install --no-install-recommends -y curl libjemalloc2 libvips sqlite3 && \
|
||||
apt-get install --no-install-recommends -y curl libjemalloc2 libvips sqlite3 libssl-dev && \
|
||||
ln -s /usr/lib/$(uname -m)-linux-gnu/libjemalloc.so.2 /usr/local/lib/libjemalloc.so && \
|
||||
rm -rf /var/lib/apt/lists /var/cache/apt/archives
|
||||
|
||||
@@ -59,6 +59,13 @@ RUN SECRET_KEY_BASE_DUMMY=1 ./bin/rails assets:precompile
|
||||
# Final stage for app image
|
||||
FROM base
|
||||
|
||||
# Image metadata
|
||||
ARG OCI_DESCRIPTION
|
||||
LABEL org.opencontainers.image.description="${OCI_DESCRIPTION}"
|
||||
ARG OCI_SOURCE
|
||||
LABEL org.opencontainers.image.source="${OCI_SOURCE}"
|
||||
LABEL org.opencontainers.image.licenses="O'Saasy"
|
||||
|
||||
# Run and own only the runtime files as a non-root user for security
|
||||
RUN groupadd --system --gid 1000 rails && \
|
||||
useradd rails --uid 1000 --gid 1000 --create-home --shell /bin/bash
|
||||
|
||||
@@ -189,9 +189,8 @@
|
||||
text-align: center;
|
||||
|
||||
kbd {
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
inset: 0.66em 0.33em auto auto;
|
||||
line-height: 1.4;
|
||||
opacity: 0.5;
|
||||
position: absolute;
|
||||
|
||||
|
||||
@@ -4,16 +4,12 @@ import { onNextEventLoopTick } from "helpers/timing_helpers"
|
||||
export default class extends Controller {
|
||||
static targets = [ "input" ]
|
||||
|
||||
submit(event) {
|
||||
submit() {
|
||||
onNextEventLoopTick(() => {
|
||||
this.inputTarget.disabled = true
|
||||
})
|
||||
}
|
||||
|
||||
paste(event) {
|
||||
onNextEventLoopTick(() => {
|
||||
this.element.submit()
|
||||
this.inputTarget.disabled = true
|
||||
if (!this.inputTarget.disabled) {
|
||||
this.element.submit()
|
||||
this.inputTarget.disabled = true
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,5 +3,6 @@ module Board::Broadcastable
|
||||
|
||||
included do
|
||||
broadcasts_refreshes
|
||||
broadcasts_refreshes_to ->(board) { [ board.account, :all_boards ] }
|
||||
end
|
||||
end
|
||||
|
||||
@@ -24,10 +24,16 @@ class User < ApplicationRecord
|
||||
transaction do
|
||||
accesses.destroy_all
|
||||
update! active: false, identity: nil
|
||||
close_remote_connections
|
||||
end
|
||||
end
|
||||
|
||||
def setup?
|
||||
name != identity.email_address
|
||||
end
|
||||
|
||||
private
|
||||
def close_remote_connections
|
||||
ActionCable.server.remote_connections.where(current_user: self).disconnect(reconnect: false)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -3,5 +3,5 @@
|
||||
<%= turbo_stream_from board %>
|
||||
<% end %>
|
||||
<% else %>
|
||||
<%= turbo_stream_from :all_boards %>
|
||||
<%= turbo_stream_from [ Current.account, :all_boards ] %>
|
||||
<% end %>
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<%= form.text_field :code, required: true, class: "input center txt-align-enter txt-large",
|
||||
autofocus: true, autocorrect: "off", autocapitalize: "off", spellcheck: "false", "data-1p-ignore": true,
|
||||
autocomplete: "one-time-code", maxlength: "6", placeholder: "••••••", value: params[:code],
|
||||
data: { magic_link_target: "input", action: "keydown.enter->magic-link#submit paste->magic-link#paste" } %>
|
||||
data: { magic_link_target: "input", action: "keydown.enter->magic-link#submit paste->magic-link#submit" } %>
|
||||
<% end %>
|
||||
|
||||
<p class="txt-small">The code you receive will work for <%= distance_of_time_in_words(MagicLink::EXPIRATION_TIME) %>.</p>
|
||||
|
||||
@@ -25,7 +25,10 @@ class UserTest < ActiveSupport::TestCase
|
||||
test "deactivate" do
|
||||
assert_changes -> { users(:jz).active? }, from: true, to: false do
|
||||
assert_changes -> { users(:jz).accesses.count }, from: 1, to: 0 do
|
||||
users(:jz).deactivate
|
||||
users(:jz).tap do |user|
|
||||
user.stubs(:close_remote_connections).once
|
||||
user.deactivate
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user